diff --git a/crates/fj-proc/src/attributed_arguments.rs b/crates/fj-proc/src/attributed_arguments.rs deleted file mode 100644 index b331a5520..000000000 --- a/crates/fj-proc/src/attributed_arguments.rs +++ /dev/null @@ -1,204 +0,0 @@ -use proc_macro::TokenStream; -use quote::quote; -use syn::{ - bracketed, parenthesized, parse::Parse, parse_macro_input, parse_quote, -}; - -pub fn attributed_arguments(_: TokenStream, input: TokenStream) -> TokenStream { - let item = parse_macro_input!(input as syn::ItemFn); - let inputs = item.clone().sig.inputs; - - let args: Vec = - inputs.iter().map(|inp| parse_quote!(#inp)).collect(); - - let mut parameter_extraction = Vec::new(); - - let mut min_checks = Vec::new(); - let mut max_checks = Vec::new(); - for arg in args { - let ident = arg.ident; - let ty = arg.ty; - if let Some(attr) = arg.attr { - if let Some(default) = 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) = 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); - } - }); - } - if let Some(maximum) = 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); - } - }) - } - } 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()); - }); - } - } - let block = item.block; - - let function_boilerplate = quote! { - #[no_mangle] - pub extern "C" fn model(args: &HashMap) -> fj::Shape - }; - - quote! { - #function_boilerplate { - #( - #parameter_extraction - )* - #( - #min_checks - )* - #( - #max_checks - )* - #block - } - } - .into() -} - -/// Represents one parameter given to the `model` -/// `#[value(default=3, min=4)] num_points: u64` -/// `^^^^^^^^^^^^^^^^^^^^^^^^^^ ~~~~~~~~~~ ^^^-- ty` -/// ` | |` -/// ` attr ident` -#[derive(Debug, Clone)] -struct Argument { - pub attr: Option, - pub ident: proc_macro2::Ident, - pub ty: proc_macro2::Ident, -} - -impl Parse for Argument { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let mut attr = None; - if input.peek(syn::token::Pound) { - attr = Some(input.parse()?); - } - let ident: proc_macro2::Ident = input.parse()?; - - let _: syn::token::Colon = input.parse()?; - - let ty: proc_macro2::Ident = input.parse()?; - Ok(Self { attr, ident, ty }) - } -} - -/// Represents all arguments given to the `#[value]` attribute eg: -/// `#[value(default=3, min=4)]` -/// ` ^^^^^^^^^^^^^^^^` -#[derive(Debug, Clone)] -struct HelperAttribute { - pub values: - Option>, -} - -impl Parse for HelperAttribute { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let attr_content; - let value_content; - let _: syn::token::Pound = input.parse()?; - bracketed!(attr_content in input); - let ident: proc_macro2::Ident = attr_content.parse()?; - if ident != *"value" { - return Err(syn::Error::new_spanned( - ident.clone(), - format!( - "Unknown attribute \"{}\" found, expected \"value\"", - ident - ), - )); - } - - 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") - } -} - -/// Represents one argument given to the `#[value]` attribute eg: -/// `#[value(default=3)]` -/// ` ^^^^^^^^^----- is parsed as DefaultParam{ ident: Some(default), val: 3 }` -#[derive(Debug, Clone)] -struct DefaultParam { - 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: proc_macro2::Ident = input.parse()?; - let _: syn::token::Eq = input.parse()?; - Ok(Self { - ident, - val: input.parse()?, - }) - } else { - Err(input - .parse::() - .expect_err("No identifier found")) - } - } -} diff --git a/crates/fj-proc/src/lib.rs b/crates/fj-proc/src/lib.rs index 915630d14..6432a25b0 100644 --- a/crates/fj-proc/src/lib.rs +++ b/crates/fj-proc/src/lib.rs @@ -1,8 +1,207 @@ use proc_macro::TokenStream; - -mod attributed_arguments; +use quote::quote; +use syn::{ + bracketed, parenthesized, parse::Parse, parse_macro_input, parse_quote, +}; #[proc_macro_attribute] -pub fn model(default_values: TokenStream, input: TokenStream) -> TokenStream { - attributed_arguments::attributed_arguments(default_values, input) +pub fn model(_: TokenStream, input: TokenStream) -> TokenStream { + let item = parse_macro_input!(input as syn::ItemFn); + let inputs = item.clone().sig.inputs; + + let args: Vec = + inputs.iter().map(|inp| parse_quote!(#inp)).collect(); + + let mut parameter_extraction = Vec::new(); + + let mut min_checks = Vec::new(); + let mut max_checks = Vec::new(); + for arg in args { + let ident = arg.ident; + let ty = arg.ty; + if let Some(attr) = arg.attr { + if let Some(default) = 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) = 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); + } + }); + } + if let Some(maximum) = 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); + } + }) + } + } 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()); + }); + } + } + let block = item.block; + + let function_boilerplate = quote! { + #[no_mangle] + pub extern "C" fn model( + args: &std::collections::HashMap + ) -> fj::Shape + }; + + quote! { + #function_boilerplate { + #( + #parameter_extraction + )* + #( + #min_checks + )* + #( + #max_checks + )* + #block + } + } + .into() +} + +/// Represents one parameter given to the `model` +/// `#[value(default=3, min=4)] num_points: u64` +/// `^^^^^^^^^^^^^^^^^^^^^^^^^^ ~~~~~~~~~~ ^^^-- ty` +/// ` | |` +/// ` attr ident` +#[derive(Debug, Clone)] +struct Argument { + pub attr: Option, + pub ident: proc_macro2::Ident, + pub ty: proc_macro2::Ident, +} + +impl Parse for Argument { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let mut attr = None; + if input.peek(syn::token::Pound) { + attr = Some(input.parse()?); + } + let ident: proc_macro2::Ident = input.parse()?; + + let _: syn::token::Colon = input.parse()?; + + let ty: proc_macro2::Ident = input.parse()?; + Ok(Self { attr, ident, ty }) + } +} + +/// Represents all arguments given to the `#[value]` attribute eg: +/// `#[value(default=3, min=4)]` +/// ` ^^^^^^^^^^^^^^^^` +#[derive(Debug, Clone)] +struct HelperAttribute { + pub values: + Option>, +} + +impl Parse for HelperAttribute { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let attr_content; + let value_content; + let _: syn::token::Pound = input.parse()?; + bracketed!(attr_content in input); + let ident: proc_macro2::Ident = attr_content.parse()?; + if ident != *"value" { + return Err(syn::Error::new_spanned( + ident.clone(), + format!( + "Unknown attribute \"{}\" found, expected \"value\"", + ident + ), + )); + } + + 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") + } +} + +/// Represents one argument given to the `#[value]` attribute eg: +/// `#[value(default=3)]` +/// ` ^^^^^^^^^----- is parsed as DefaultParam{ ident: Some(default), val: 3 }` +#[derive(Debug, Clone)] +struct DefaultParam { + 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: proc_macro2::Ident = input.parse()?; + let _: syn::token::Eq = input.parse()?; + Ok(Self { + ident, + val: input.parse()?, + }) + } else { + Err(input + .parse::() + .expect_err("No identifier found")) + } + } } diff --git a/models/cuboid/src/lib.rs b/models/cuboid/src/lib.rs index 59a09995f..6ce903ae5 100644 --- a/models/cuboid/src/lib.rs +++ b/models/cuboid/src/lib.rs @@ -1,5 +1,3 @@ -use std::collections::HashMap; - #[fj::model] pub fn model( #[value(default = 3.0)] x: f64, diff --git a/models/spacer/src/lib.rs b/models/spacer/src/lib.rs index 7cb687eb8..385ad5328 100644 --- a/models/spacer/src/lib.rs +++ b/models/spacer/src/lib.rs @@ -1,5 +1,3 @@ -use std::collections::HashMap; - use fj::syntax::*; #[fj::model] diff --git a/models/star/src/lib.rs b/models/star/src/lib.rs index e8e29d2aa..b8af50107 100644 --- a/models/star/src/lib.rs +++ b/models/star/src/lib.rs @@ -1,5 +1,5 @@ use fj::Angle; -use std::{collections::HashMap, f64::consts::PI}; +use std::f64::consts::PI; extern crate fj; #[fj::model] diff --git a/models/test/src/lib.rs b/models/test/src/lib.rs index cf4ce09c2..c35fa16af 100644 --- a/models/test/src/lib.rs +++ b/models/test/src/lib.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, f64::consts::PI}; +use std::f64::consts::PI; use fj::{syntax::*, Angle};