From 2c00d7fb791c64e14530d9c5e14b9cb65ebf2913 Mon Sep 17 00:00:00 2001 From: gabsi26 Date: Tue, 31 May 2022 17:10:55 +0200 Subject: [PATCH 01/11] Remove unneeded model Signed-off-by: gabsi26 --- models/star_attributed_arguments/Cargo.toml | 13 ------- models/star_attributed_arguments/src/lib.rs | 41 --------------------- 2 files changed, 54 deletions(-) delete mode 100644 models/star_attributed_arguments/Cargo.toml delete mode 100644 models/star_attributed_arguments/src/lib.rs diff --git a/models/star_attributed_arguments/Cargo.toml b/models/star_attributed_arguments/Cargo.toml deleted file mode 100644 index a22353e9a..000000000 --- a/models/star_attributed_arguments/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "star_attributed_arguments" -version = "0.1.0" -edition = "2021" - -[lib] -crate-type = ["cdylib"] - -[dependencies.fj] -path = "../../crates/fj" - -[dependencies.fj-proc] -path = "../../crates/fj-proc" \ No newline at end of file diff --git a/models/star_attributed_arguments/src/lib.rs b/models/star_attributed_arguments/src/lib.rs deleted file mode 100644 index 5a4364b32..000000000 --- a/models/star_attributed_arguments/src/lib.rs +++ /dev/null @@ -1,41 +0,0 @@ -use fj::Angle; -use std::{collections::HashMap, f64::consts::PI}; -extern crate fj_proc; - -#[fj_proc::attributed_arguments] -pub fn model( - #[value(default = 5, min = 3, max = 100)] num_points: u64, - #[value(default = 1.0, min = 1.0)] r1: f64, - #[value(min = 2.0)] r2: f64, - #[value(1.0)] h: f64, -) -> fj::Shape { - let num_vertices = num_points * 2; - let vertex_iter = (0..num_vertices).map(|i| { - let angle = Angle::from_rad(2. * PI / num_vertices as f64 * i as f64); - let radius = if i % 2 == 0 { r1 } else { r2 }; - (angle, radius) - }); - - // Now that we got that iterator prepared, generating the vertices is just a - // bit of trigonometry. - let mut outer = Vec::new(); - let mut inner = Vec::new(); - for (angle, radius) in vertex_iter { - let (sin, cos) = angle.rad().sin_cos(); - - let x = cos * radius; - let y = sin * radius; - - outer.push([x, y]); - inner.push([x / 2., y / 2.]); - } - - let outer = fj::Sketch::from_points(outer); - let inner = fj::Sketch::from_points(inner); - - let footprint = fj::Difference2d::from_shapes([outer.into(), inner.into()]); - - let star = fj::Sweep::from_path(footprint.into(), [0., 0., -h]); - - star.into() -} From 7b527750c58bc271abade5ea7a56e924cc5809d3 Mon Sep 17 00:00:00 2001 From: gabsi26 Date: Tue, 31 May 2022 17:11:18 +0200 Subject: [PATCH 02/11] Clean up workspace Signed-off-by: gabsi26 --- Cargo.toml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 274f300e4..d9114c8d4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,18 +9,13 @@ members = [ "crates/fj-kernel", "crates/fj-math", "crates/fj-operations", - "crates/fj-viewer", "crates/fj-proc", - + "crates/fj-viewer", "crates/fj-window", - "crates/fj-proc", - - "models/cuboid", "models/spacer", "models/star", - "models/star_attributed_arguments", "models/test", "tools/export-validator", From ec6d4e7b5646bedbf493e05ff055006cab07b629 Mon Sep 17 00:00:00 2001 From: gabsi26 Date: Tue, 31 May 2022 17:12:47 +0200 Subject: [PATCH 03/11] Add `fj-proc` dependency to `fj`to reexport macros Signed-off-by: gabsi26 --- Cargo.lock | 10 +--------- crates/fj/Cargo.toml | 3 +++ crates/fj/src/lib.rs | 1 + 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0c6a983db..667a8a76a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -690,6 +690,7 @@ dependencies = [ name = "fj" version = "0.6.0" dependencies = [ + "fj-proc", "serde", ] @@ -2478,15 +2479,6 @@ name = "star" version = "0.1.0" dependencies = [ "fj", - "fj-proc", -] - -[[package]] -name = "star_attributed_arguments" -version = "0.1.0" -dependencies = [ - "fj", - "fj-proc", ] [[package]] diff --git a/crates/fj/Cargo.toml b/crates/fj/Cargo.toml index 60992884e..b197605e6 100644 --- a/crates/fj/Cargo.toml +++ b/crates/fj/Cargo.toml @@ -17,3 +17,6 @@ serialization = ["serde"] [dependencies] serde = { version = "1.0.7", optional = true } + +[dependencies.fj-proc] +path = "../../crates/fj-proc" \ No newline at end of file diff --git a/crates/fj/src/lib.rs b/crates/fj/src/lib.rs index f5c83ede7..c6cdb061d 100644 --- a/crates/fj/src/lib.rs +++ b/crates/fj/src/lib.rs @@ -25,6 +25,7 @@ mod shape_2d; mod shape_3d; pub use self::{angle::*, shape_2d::*, shape_3d::*}; +pub use fj_proc::*; /// A shape #[derive(Clone, Debug)] From 1d6b1cdc32dbe8ecb8c522d32573cb7e0d90ac4e Mon Sep 17 00:00:00 2001 From: gabsi26 Date: Tue, 31 May 2022 17:16:24 +0200 Subject: [PATCH 04/11] Clean up and improve `model` proc-macro If no `default` is supplied now the parameter needs to be given. `min` and `max` no longer contribute as replacement `default`. Automatic checks for minimum and maximum added Signed-off-by: gabsi26 --- crates/fj-proc/src/attributed_arguments.rs | 210 ++++++++++----------- crates/fj-proc/src/lib.rs | 68 ------- 2 files changed, 103 insertions(+), 175 deletions(-) diff --git a/crates/fj-proc/src/attributed_arguments.rs b/crates/fj-proc/src/attributed_arguments.rs index d2deccdf2..f277dc6d7 100644 --- a/crates/fj-proc/src/attributed_arguments.rs +++ b/crates/fj-proc/src/attributed_arguments.rs @@ -11,45 +11,68 @@ pub fn attributed_arguments(_: TokenStream, input: TokenStream) -> TokenStream { let args: Vec = inputs.iter().map(|inp| parse_quote!(#inp)).collect(); - let mut defaults = Vec::new(); - let mut mins = Vec::new(); - let mut maxs = Vec::new(); - let mut names = Vec::new(); - let mut types = Vec::new(); + let mut parameter_extraction = Vec::new(); + + let mut min_checks = Vec::new(); + let mut max_checks = Vec::new(); for arg in args { - let mut default = None; - let mut min = None; - let mut max = None; - names.push(arg.ident); - types.push(arg.ty); - for value in arg.attr.values.clone() { - if let Some(ident) = value.ident.clone() { - match ident.to_string().as_str() { - "default" => default = Some(value.val.clone()), - "min" => min = Some(value.val.clone()), - "max" => max = Some(value.val.clone()), - _ => {} + let ident = arg.ident; + let ty = arg.ty; + + if let Some(default) = arg.attr.get_default() { + let def = default.val; + parameter_extraction.push(quote! { + let #ident: #ty = args.get(stringify!(#ident)) + .map(|arg| arg.parse().unwrap()) + .unwrap_or(#def); + }); + } else { + parameter_extraction.push(quote! { + let #ident: #ty = args.get(stringify!(#ident)) + .map(|arg| arg.parse().unwrap()) + .expect(format!("A value for `{}` has to be provided since no default is specified",stringify!(#ident)).as_str()); + }); + } + + if let Some(minimum) = arg.attr.get_minimum() { + let min = minimum.val; + min_checks.push(quote! { + if #ident < #min { + panic!("Value of `{}` must not be smaller than: {}",stringify!(#ident), #min); } - } else { - default = Some(value.val.clone()); - } + }); + } + if let Some(maximum) = arg.attr.get_maximum() { + let max = maximum.val; + max_checks.push(quote! { + if #ident > #max { + panic!("Value of `{}` must not be larger than: {}", stringify!(#ident), #max); + } + }) } - let [default, min, max] = determine_default(default, min, max); - defaults.push(default); - mins.push(min); - maxs.push(max); } let block = item.block; + let function_boilerplate = quote! { + #[no_mangle] + pub extern "C" fn model(args: &HashMap) -> fj::Shape + }; + quote! { - #[no_mangle] - pub extern "C" fn model(args: &HashMap) -> fj::Shape { - #( - let #names: #types = args.get(stringify!(#names)).map(|arg| arg.parse().unwrap()).unwrap_or(#defaults); - )* - #block - } - }.into() + #function_boilerplate { + #( + #parameter_extraction + )* + #( + #min_checks + )* + #( + #max_checks + )* + #block + } + } + .into() } /// Represents one parameter given to the `model` @@ -81,7 +104,8 @@ impl Parse for Argument { /// ` ^^^^^^^^^^^^^^^^` #[derive(Debug, Clone)] struct HelperAttribute { - pub values: syn::punctuated::Punctuated, + pub values: + Option>, } impl Parse for HelperAttribute { @@ -91,7 +115,7 @@ impl Parse for HelperAttribute { let _: syn::token::Pound = input.parse()?; bracketed!(attr_content in input); let ident: proc_macro2::Ident = attr_content.parse()?; - if ident.to_string() != *"value" { + if ident != *"value" { return Err(syn::Error::new_spanned( ident.clone(), format!( @@ -100,14 +124,46 @@ impl Parse for HelperAttribute { ), )); } - parenthesized!(value_content in attr_content); - - Ok(Self { - values: syn::punctuated::Punctuated::parse_separated_nonempty_with( - &value_content, - DefaultParam::parse, - )?, - }) + + if attr_content.peek(syn::token::Paren) { + parenthesized!(value_content in attr_content); + if value_content.is_empty() { + Ok(Self { values: None }) + } else { + Ok(Self { + values: Some( + syn::punctuated::Punctuated::parse_separated_nonempty_with( + &value_content, + DefaultParam::parse, + )?, + ), + }) + } + } else { + Ok(Self { values: None }) + } + } +} + +impl HelperAttribute { + fn get_parameter(&self, parameter_name: &str) -> Option { + if let Some(values) = self.values.clone() { + values.into_iter().find(|val| val.ident == *parameter_name) + } else { + None + } + } + + pub fn get_default(&self) -> Option { + self.get_parameter("default") + } + + pub fn get_minimum(&self) -> Option { + self.get_parameter("min") + } + + pub fn get_maximum(&self) -> Option { + self.get_parameter("max") } } @@ -116,83 +172,23 @@ impl Parse for HelperAttribute { /// ` ^^^^^^^^^----- is parsed as DefaultParam{ ident: Some(default), val: 3 }` #[derive(Debug, Clone)] struct DefaultParam { - pub ident: Option, + pub ident: proc_macro2::Ident, pub val: syn::Expr, } impl Parse for DefaultParam { fn parse(input: syn::parse::ParseStream) -> syn::Result { if input.peek(syn::Ident) { - let ident: Option = Some(input.parse()?); + let ident: proc_macro2::Ident = input.parse()?; let _: syn::token::Eq = input.parse()?; Ok(Self { ident, val: input.parse()?, }) } else { - Ok(Self { - ident: None, - val: input.parse()?, - }) + Err(input + .parse::() + .expect_err("No identifier found")) } } } - -/// Checks if a default value is supplied, otherwise applies either the min or max (if specified) as default. -fn determine_default( - default: Option, - min: Option, - max: Option, -) -> [Option; 3] { - if let Some(default) = default { - let min = if min.is_some() { min } else { None }; - let max = if max.is_some() { max } else { None }; - [Some(default), min, max] - } else { - let mut default = None; - let max = if max.is_some() { - default = max.clone(); - max - } else { - None - }; - - let min = if min.is_some() { - default = min.clone(); - min - } else { - None - }; - - [default, min, max] - } -} - -// #[fj::model] -// pub fn model( -// #[default(5)] num_points: u64, -// #[default(1.0)] r1: f64, -// #[default(2.0)] r2: f64, -// #[default(1.0)] h: f64, -// ) -> fj::Shape - -// #[no_mangle] -// pub extern "C" fn model(args: &HashMap) -> fj::Shape { -// let num_points: u64 = args -// .get("num_points") -// .map(|arg| arg.parse().unwrap()) -// .unwrap_or(5); - -// let r1: f64 = args -// .get("r1") -// .map(|arg| arg.parse().unwrap()) -// .unwrap_or(1.0); - -// let r2: f64 = args -// .get("r2") -// .map(|arg| arg.parse().unwrap()) -// .unwrap_or(2.0); - -// let h: f64 = args.get("h").map(|arg| arg.parse().unwrap()).unwrap_or(1.0); - -// } diff --git a/crates/fj-proc/src/lib.rs b/crates/fj-proc/src/lib.rs index eebdaf37b..915630d14 100644 --- a/crates/fj-proc/src/lib.rs +++ b/crates/fj-proc/src/lib.rs @@ -1,76 +1,8 @@ use proc_macro::TokenStream; -use quote::quote; -use syn::parse_macro_input; mod attributed_arguments; #[proc_macro_attribute] pub fn model(default_values: TokenStream, input: TokenStream) -> TokenStream { - let vals: Vec = default_values - .into_iter() - .filter_map(|tree| { - if let proc_macro::TokenTree::Literal(lit) = tree { - Some(lit.to_string()) - } else { - None - } - }) - .collect(); - let item = parse_macro_input!(input as syn::ItemFn); - - let inputs = item.sig.inputs; - let mut names = Vec::new(); - let mut types = Vec::new(); - for f in inputs.iter() { - if let syn::FnArg::Typed(meep) = f { - if let syn::Pat::Ident(ident) = *meep.clone().pat { - names.push(ident.ident); - } - if let syn::Type::Path(path) = *meep.clone().ty { - types.push(path.path.get_ident().unwrap().clone()); - } - } - } - let block = item.block; - - quote! { - #[no_mangle] - pub extern "C" fn model(args: &HashMap) -> fj::Shape { - #(let #names: #types = args.get(stringify!(#names)).map(|arg| arg.parse().unwrap()).unwrap_or(#vals.parse::<#types>().unwrap());)* - #block - } - }.into() -} - -#[proc_macro_attribute] -pub fn attributed_arguments( - default_values: TokenStream, - input: TokenStream, -) -> TokenStream { attributed_arguments::attributed_arguments(default_values, input) } - -// #[fj_proc::model(5, 1.0, 2.0, 1.0)] -// pub fn model(num_points: u64, r1: f64, r2: f64, h: f64) -> fj::Shape { -// } - -// #[no_mangle] -// pub extern "C" fn model(args: &HashMap) -> fj::Shape { -// let num_points: u64 = args -// .get("num_points") -// .map(|arg| arg.parse().unwrap()) -// .unwrap_or(5); - -// let r1: f64 = args -// .get("r1") -// .map(|arg| arg.parse().unwrap()) -// .unwrap_or(1.0); - -// let r2: f64 = args -// .get("r2") -// .map(|arg| arg.parse().unwrap()) -// .unwrap_or(2.0); - -// let h: f64 = args.get("h").map(|arg| arg.parse().unwrap()).unwrap_or(1.0); - -// } From 10d9e94f1d4b2d5bfb19cc5dcd4df8bd9b8018bb Mon Sep 17 00:00:00 2001 From: gabsi26 Date: Tue, 31 May 2022 17:16:45 +0200 Subject: [PATCH 05/11] Update `star`-model Signed-off-by: gabsi26 --- models/star/Cargo.toml | 3 --- models/star/src/lib.rs | 15 ++++++++++----- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/models/star/Cargo.toml b/models/star/Cargo.toml index 658366623..c5c2d4efd 100644 --- a/models/star/Cargo.toml +++ b/models/star/Cargo.toml @@ -8,6 +8,3 @@ crate-type = ["cdylib"] [dependencies.fj] path = "../../crates/fj" - -[dependencies.fj-proc] -path = "../../crates/fj-proc" \ No newline at end of file diff --git a/models/star/src/lib.rs b/models/star/src/lib.rs index cb8691be1..565aac069 100644 --- a/models/star/src/lib.rs +++ b/models/star/src/lib.rs @@ -1,9 +1,14 @@ use fj::Angle; use std::{collections::HashMap, f64::consts::PI}; -extern crate fj_proc; - -#[fj_proc::model(5, 1.0, 2.0, 1.0)] -pub fn model(num_points: u64, r1: f64, r2: f64, h: f64) -> fj::Shape { +extern crate fj; + +#[fj::model] +pub fn model( + #[value(default = 5, min = 3)] num_points: u64, + #[value(default = 1.0, min = 1.0)] r1: f64, + #[value(default = 2.0, min = 2.0)] r2: f64, + #[value] h: f64, +) -> fj::Shape { let num_vertices = num_points * 2; let vertex_iter = (0..num_vertices).map(|i| { let angle = Angle::from_rad(2. * PI / num_vertices as f64 * i as f64); @@ -30,7 +35,7 @@ pub fn model(num_points: u64, r1: f64, r2: f64, h: f64) -> fj::Shape { let footprint = fj::Difference2d::from_shapes([outer.into(), inner.into()]); - let star = fj::Sweep::from_path(footprint.into(), [0., 0., -h]); + let star = fj::Sweep::from_path(footprint.into(), [0., 0., h]); star.into() } From b7062eb0ee1b7af88e120d722d8c5ecd77e67e62 Mon Sep 17 00:00:00 2001 From: gabsi26 Date: Tue, 31 May 2022 17:10:55 +0200 Subject: [PATCH 06/11] Remove unneeded model Signed-off-by: gabsi26 --- models/star_attributed_arguments/Cargo.toml | 13 ------- models/star_attributed_arguments/src/lib.rs | 41 --------------------- 2 files changed, 54 deletions(-) delete mode 100644 models/star_attributed_arguments/Cargo.toml delete mode 100644 models/star_attributed_arguments/src/lib.rs diff --git a/models/star_attributed_arguments/Cargo.toml b/models/star_attributed_arguments/Cargo.toml deleted file mode 100644 index a22353e9a..000000000 --- a/models/star_attributed_arguments/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "star_attributed_arguments" -version = "0.1.0" -edition = "2021" - -[lib] -crate-type = ["cdylib"] - -[dependencies.fj] -path = "../../crates/fj" - -[dependencies.fj-proc] -path = "../../crates/fj-proc" \ No newline at end of file diff --git a/models/star_attributed_arguments/src/lib.rs b/models/star_attributed_arguments/src/lib.rs deleted file mode 100644 index 5a4364b32..000000000 --- a/models/star_attributed_arguments/src/lib.rs +++ /dev/null @@ -1,41 +0,0 @@ -use fj::Angle; -use std::{collections::HashMap, f64::consts::PI}; -extern crate fj_proc; - -#[fj_proc::attributed_arguments] -pub fn model( - #[value(default = 5, min = 3, max = 100)] num_points: u64, - #[value(default = 1.0, min = 1.0)] r1: f64, - #[value(min = 2.0)] r2: f64, - #[value(1.0)] h: f64, -) -> fj::Shape { - let num_vertices = num_points * 2; - let vertex_iter = (0..num_vertices).map(|i| { - let angle = Angle::from_rad(2. * PI / num_vertices as f64 * i as f64); - let radius = if i % 2 == 0 { r1 } else { r2 }; - (angle, radius) - }); - - // Now that we got that iterator prepared, generating the vertices is just a - // bit of trigonometry. - let mut outer = Vec::new(); - let mut inner = Vec::new(); - for (angle, radius) in vertex_iter { - let (sin, cos) = angle.rad().sin_cos(); - - let x = cos * radius; - let y = sin * radius; - - outer.push([x, y]); - inner.push([x / 2., y / 2.]); - } - - let outer = fj::Sketch::from_points(outer); - let inner = fj::Sketch::from_points(inner); - - let footprint = fj::Difference2d::from_shapes([outer.into(), inner.into()]); - - let star = fj::Sweep::from_path(footprint.into(), [0., 0., -h]); - - star.into() -} From 5149392100d85add8da3bc9d874d2942ce39c123 Mon Sep 17 00:00:00 2001 From: gabsi26 Date: Tue, 31 May 2022 17:11:18 +0200 Subject: [PATCH 07/11] Clean up workspace Signed-off-by: gabsi26 --- Cargo.toml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 274f300e4..d9114c8d4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,18 +9,13 @@ members = [ "crates/fj-kernel", "crates/fj-math", "crates/fj-operations", - "crates/fj-viewer", "crates/fj-proc", - + "crates/fj-viewer", "crates/fj-window", - "crates/fj-proc", - - "models/cuboid", "models/spacer", "models/star", - "models/star_attributed_arguments", "models/test", "tools/export-validator", From c95acea655db94da87e8a5341d6b3bb5d898f41b Mon Sep 17 00:00:00 2001 From: gabsi26 Date: Tue, 31 May 2022 17:12:47 +0200 Subject: [PATCH 08/11] Add `fj-proc` dependency to `fj`to reexport macros Signed-off-by: gabsi26 --- Cargo.lock | 10 +--------- crates/fj/Cargo.toml | 3 +++ crates/fj/src/lib.rs | 1 + 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0c6a983db..667a8a76a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -690,6 +690,7 @@ dependencies = [ name = "fj" version = "0.6.0" dependencies = [ + "fj-proc", "serde", ] @@ -2478,15 +2479,6 @@ name = "star" version = "0.1.0" dependencies = [ "fj", - "fj-proc", -] - -[[package]] -name = "star_attributed_arguments" -version = "0.1.0" -dependencies = [ - "fj", - "fj-proc", ] [[package]] diff --git a/crates/fj/Cargo.toml b/crates/fj/Cargo.toml index 60992884e..b197605e6 100644 --- a/crates/fj/Cargo.toml +++ b/crates/fj/Cargo.toml @@ -17,3 +17,6 @@ serialization = ["serde"] [dependencies] serde = { version = "1.0.7", optional = true } + +[dependencies.fj-proc] +path = "../../crates/fj-proc" \ No newline at end of file diff --git a/crates/fj/src/lib.rs b/crates/fj/src/lib.rs index f5c83ede7..c6cdb061d 100644 --- a/crates/fj/src/lib.rs +++ b/crates/fj/src/lib.rs @@ -25,6 +25,7 @@ mod shape_2d; mod shape_3d; pub use self::{angle::*, shape_2d::*, shape_3d::*}; +pub use fj_proc::*; /// A shape #[derive(Clone, Debug)] From abd8609d1494c16f406f0f66bc627901fab02898 Mon Sep 17 00:00:00 2001 From: gabsi26 Date: Tue, 31 May 2022 17:16:24 +0200 Subject: [PATCH 09/11] Clean up and improve `model` proc-macro If no `default` is supplied now the parameter needs to be given. `min` and `max` no longer contribute as replacement `default`. Automatic checks for minimum and maximum added Signed-off-by: gabsi26 --- crates/fj-proc/src/attributed_arguments.rs | 208 ++++++++++----------- crates/fj-proc/src/lib.rs | 68 ------- 2 files changed, 102 insertions(+), 174 deletions(-) diff --git a/crates/fj-proc/src/attributed_arguments.rs b/crates/fj-proc/src/attributed_arguments.rs index d016d435a..f277dc6d7 100644 --- a/crates/fj-proc/src/attributed_arguments.rs +++ b/crates/fj-proc/src/attributed_arguments.rs @@ -11,45 +11,68 @@ pub fn attributed_arguments(_: TokenStream, input: TokenStream) -> TokenStream { let args: Vec = inputs.iter().map(|inp| parse_quote!(#inp)).collect(); - let mut defaults = Vec::new(); - let mut mins = Vec::new(); - let mut maxs = Vec::new(); - let mut names = Vec::new(); - let mut types = Vec::new(); + let mut parameter_extraction = Vec::new(); + + let mut min_checks = Vec::new(); + let mut max_checks = Vec::new(); for arg in args { - let mut default = None; - let mut min = None; - let mut max = None; - names.push(arg.ident); - types.push(arg.ty); - for value in arg.attr.values.clone() { - if let Some(ident) = value.ident.clone() { - match ident.to_string().as_str() { - "default" => default = Some(value.val.clone()), - "min" => min = Some(value.val.clone()), - "max" => max = Some(value.val.clone()), - _ => {} + let ident = arg.ident; + let ty = arg.ty; + + if let Some(default) = arg.attr.get_default() { + let def = default.val; + parameter_extraction.push(quote! { + let #ident: #ty = args.get(stringify!(#ident)) + .map(|arg| arg.parse().unwrap()) + .unwrap_or(#def); + }); + } else { + parameter_extraction.push(quote! { + let #ident: #ty = args.get(stringify!(#ident)) + .map(|arg| arg.parse().unwrap()) + .expect(format!("A value for `{}` has to be provided since no default is specified",stringify!(#ident)).as_str()); + }); + } + + if let Some(minimum) = arg.attr.get_minimum() { + let min = minimum.val; + min_checks.push(quote! { + if #ident < #min { + panic!("Value of `{}` must not be smaller than: {}",stringify!(#ident), #min); } - } else { - default = Some(value.val.clone()); - } + }); + } + if let Some(maximum) = arg.attr.get_maximum() { + let max = maximum.val; + max_checks.push(quote! { + if #ident > #max { + panic!("Value of `{}` must not be larger than: {}", stringify!(#ident), #max); + } + }) } - let [default, min, max] = determine_default(default, min, max); - defaults.push(default); - mins.push(min); - maxs.push(max); } let block = item.block; + let function_boilerplate = quote! { + #[no_mangle] + pub extern "C" fn model(args: &HashMap) -> fj::Shape + }; + quote! { - #[no_mangle] - pub extern "C" fn model(args: &HashMap) -> fj::Shape { - #( - let #names: #types = args.get(stringify!(#names)).map(|arg| arg.parse().unwrap()).unwrap_or(#defaults); - )* - #block - } - }.into() + #function_boilerplate { + #( + #parameter_extraction + )* + #( + #min_checks + )* + #( + #max_checks + )* + #block + } + } + .into() } /// Represents one parameter given to the `model` @@ -81,7 +104,8 @@ impl Parse for Argument { /// ` ^^^^^^^^^^^^^^^^` #[derive(Debug, Clone)] struct HelperAttribute { - pub values: syn::punctuated::Punctuated, + pub values: + Option>, } impl Parse for HelperAttribute { @@ -100,14 +124,46 @@ impl Parse for HelperAttribute { ), )); } - parenthesized!(value_content in attr_content); - - Ok(Self { - values: syn::punctuated::Punctuated::parse_separated_nonempty_with( - &value_content, - DefaultParam::parse, - )?, - }) + + if attr_content.peek(syn::token::Paren) { + parenthesized!(value_content in attr_content); + if value_content.is_empty() { + Ok(Self { values: None }) + } else { + Ok(Self { + values: Some( + syn::punctuated::Punctuated::parse_separated_nonempty_with( + &value_content, + DefaultParam::parse, + )?, + ), + }) + } + } else { + Ok(Self { values: None }) + } + } +} + +impl HelperAttribute { + fn get_parameter(&self, parameter_name: &str) -> Option { + if let Some(values) = self.values.clone() { + values.into_iter().find(|val| val.ident == *parameter_name) + } else { + None + } + } + + pub fn get_default(&self) -> Option { + self.get_parameter("default") + } + + pub fn get_minimum(&self) -> Option { + self.get_parameter("min") + } + + pub fn get_maximum(&self) -> Option { + self.get_parameter("max") } } @@ -116,83 +172,23 @@ impl Parse for HelperAttribute { /// ` ^^^^^^^^^----- is parsed as DefaultParam{ ident: Some(default), val: 3 }` #[derive(Debug, Clone)] struct DefaultParam { - pub ident: Option, + pub ident: proc_macro2::Ident, pub val: syn::Expr, } impl Parse for DefaultParam { fn parse(input: syn::parse::ParseStream) -> syn::Result { if input.peek(syn::Ident) { - let ident: Option = Some(input.parse()?); + let ident: proc_macro2::Ident = input.parse()?; let _: syn::token::Eq = input.parse()?; Ok(Self { ident, val: input.parse()?, }) } else { - Ok(Self { - ident: None, - val: input.parse()?, - }) + Err(input + .parse::() + .expect_err("No identifier found")) } } } - -/// Checks if a default value is supplied, otherwise applies either the min or max (if specified) as default. -fn determine_default( - default: Option, - min: Option, - max: Option, -) -> [Option; 3] { - if let Some(default) = default { - let min = if min.is_some() { min } else { None }; - let max = if max.is_some() { max } else { None }; - [Some(default), min, max] - } else { - let mut default = None; - let max = if max.is_some() { - default = max.clone(); - max - } else { - None - }; - - let min = if min.is_some() { - default = min.clone(); - min - } else { - None - }; - - [default, min, max] - } -} - -// #[fj::model] -// pub fn model( -// #[default(5)] num_points: u64, -// #[default(1.0)] r1: f64, -// #[default(2.0)] r2: f64, -// #[default(1.0)] h: f64, -// ) -> fj::Shape - -// #[no_mangle] -// pub extern "C" fn model(args: &HashMap) -> fj::Shape { -// let num_points: u64 = args -// .get("num_points") -// .map(|arg| arg.parse().unwrap()) -// .unwrap_or(5); - -// let r1: f64 = args -// .get("r1") -// .map(|arg| arg.parse().unwrap()) -// .unwrap_or(1.0); - -// let r2: f64 = args -// .get("r2") -// .map(|arg| arg.parse().unwrap()) -// .unwrap_or(2.0); - -// let h: f64 = args.get("h").map(|arg| arg.parse().unwrap()).unwrap_or(1.0); - -// } diff --git a/crates/fj-proc/src/lib.rs b/crates/fj-proc/src/lib.rs index eebdaf37b..915630d14 100644 --- a/crates/fj-proc/src/lib.rs +++ b/crates/fj-proc/src/lib.rs @@ -1,76 +1,8 @@ use proc_macro::TokenStream; -use quote::quote; -use syn::parse_macro_input; mod attributed_arguments; #[proc_macro_attribute] pub fn model(default_values: TokenStream, input: TokenStream) -> TokenStream { - let vals: Vec = default_values - .into_iter() - .filter_map(|tree| { - if let proc_macro::TokenTree::Literal(lit) = tree { - Some(lit.to_string()) - } else { - None - } - }) - .collect(); - let item = parse_macro_input!(input as syn::ItemFn); - - let inputs = item.sig.inputs; - let mut names = Vec::new(); - let mut types = Vec::new(); - for f in inputs.iter() { - if let syn::FnArg::Typed(meep) = f { - if let syn::Pat::Ident(ident) = *meep.clone().pat { - names.push(ident.ident); - } - if let syn::Type::Path(path) = *meep.clone().ty { - types.push(path.path.get_ident().unwrap().clone()); - } - } - } - let block = item.block; - - quote! { - #[no_mangle] - pub extern "C" fn model(args: &HashMap) -> fj::Shape { - #(let #names: #types = args.get(stringify!(#names)).map(|arg| arg.parse().unwrap()).unwrap_or(#vals.parse::<#types>().unwrap());)* - #block - } - }.into() -} - -#[proc_macro_attribute] -pub fn attributed_arguments( - default_values: TokenStream, - input: TokenStream, -) -> TokenStream { attributed_arguments::attributed_arguments(default_values, input) } - -// #[fj_proc::model(5, 1.0, 2.0, 1.0)] -// pub fn model(num_points: u64, r1: f64, r2: f64, h: f64) -> fj::Shape { -// } - -// #[no_mangle] -// pub extern "C" fn model(args: &HashMap) -> fj::Shape { -// let num_points: u64 = args -// .get("num_points") -// .map(|arg| arg.parse().unwrap()) -// .unwrap_or(5); - -// let r1: f64 = args -// .get("r1") -// .map(|arg| arg.parse().unwrap()) -// .unwrap_or(1.0); - -// let r2: f64 = args -// .get("r2") -// .map(|arg| arg.parse().unwrap()) -// .unwrap_or(2.0); - -// let h: f64 = args.get("h").map(|arg| arg.parse().unwrap()).unwrap_or(1.0); - -// } From 20057e431f9973025ee4b95a65dd7a0cef9e6b02 Mon Sep 17 00:00:00 2001 From: gabsi26 Date: Tue, 31 May 2022 17:16:45 +0200 Subject: [PATCH 10/11] Update `star`-model Signed-off-by: gabsi26 --- models/star/Cargo.toml | 3 --- models/star/src/lib.rs | 15 ++++++++++----- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/models/star/Cargo.toml b/models/star/Cargo.toml index 658366623..c5c2d4efd 100644 --- a/models/star/Cargo.toml +++ b/models/star/Cargo.toml @@ -8,6 +8,3 @@ crate-type = ["cdylib"] [dependencies.fj] path = "../../crates/fj" - -[dependencies.fj-proc] -path = "../../crates/fj-proc" \ No newline at end of file diff --git a/models/star/src/lib.rs b/models/star/src/lib.rs index cb8691be1..565aac069 100644 --- a/models/star/src/lib.rs +++ b/models/star/src/lib.rs @@ -1,9 +1,14 @@ use fj::Angle; use std::{collections::HashMap, f64::consts::PI}; -extern crate fj_proc; - -#[fj_proc::model(5, 1.0, 2.0, 1.0)] -pub fn model(num_points: u64, r1: f64, r2: f64, h: f64) -> fj::Shape { +extern crate fj; + +#[fj::model] +pub fn model( + #[value(default = 5, min = 3)] num_points: u64, + #[value(default = 1.0, min = 1.0)] r1: f64, + #[value(default = 2.0, min = 2.0)] r2: f64, + #[value] h: f64, +) -> fj::Shape { let num_vertices = num_points * 2; let vertex_iter = (0..num_vertices).map(|i| { let angle = Angle::from_rad(2. * PI / num_vertices as f64 * i as f64); @@ -30,7 +35,7 @@ pub fn model(num_points: u64, r1: f64, r2: f64, h: f64) -> fj::Shape { let footprint = fj::Difference2d::from_shapes([outer.into(), inner.into()]); - let star = fj::Sweep::from_path(footprint.into(), [0., 0., -h]); + let star = fj::Sweep::from_path(footprint.into(), [0., 0., h]); star.into() } From 81217025ccc2726a3c057b6f6490bc1d7add0f2c Mon Sep 17 00:00:00 2001 From: gabsi26 Date: Tue, 31 May 2022 17:26:57 +0200 Subject: [PATCH 11/11] Fix CI check by providing default value for `h` Signed-off-by: gabsi26 --- models/star/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/star/src/lib.rs b/models/star/src/lib.rs index 565aac069..e8e29d2aa 100644 --- a/models/star/src/lib.rs +++ b/models/star/src/lib.rs @@ -7,7 +7,7 @@ pub fn model( #[value(default = 5, min = 3)] num_points: u64, #[value(default = 1.0, min = 1.0)] r1: f64, #[value(default = 2.0, min = 2.0)] r2: f64, - #[value] h: f64, + #[value(default = 1.0)] h: f64, ) -> fj::Shape { let num_vertices = num_points * 2; let vertex_iter = (0..num_vertices).map(|i| {