Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proc me up again #652

Merged
merged 13 commits into from
Jun 1, 2022
10 changes: 1 addition & 9 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 1 addition & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
208 changes: 102 additions & 106 deletions crates/fj-proc/src/attributed_arguments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,45 +11,68 @@ pub fn attributed_arguments(_: TokenStream, input: TokenStream) -> TokenStream {
let args: Vec<Argument> =
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<String, String>) -> fj::Shape
};

quote! {
#[no_mangle]
pub extern "C" fn model(args: &HashMap<String, String>) -> 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`
Expand Down Expand Up @@ -81,7 +104,8 @@ impl Parse for Argument {
/// ` ^^^^^^^^^^^^^^^^`
#[derive(Debug, Clone)]
struct HelperAttribute {
pub values: syn::punctuated::Punctuated<DefaultParam, syn::Token![,]>,
pub values:
Option<syn::punctuated::Punctuated<DefaultParam, syn::Token![,]>>,
}

impl Parse for HelperAttribute {
Expand All @@ -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<DefaultParam> {
if let Some(values) = self.values.clone() {
values.into_iter().find(|val| val.ident == *parameter_name)
} else {
None
}
}

pub fn get_default(&self) -> Option<DefaultParam> {
self.get_parameter("default")
}

pub fn get_minimum(&self) -> Option<DefaultParam> {
self.get_parameter("min")
}

pub fn get_maximum(&self) -> Option<DefaultParam> {
self.get_parameter("max")
}
}

Expand All @@ -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<proc_macro2::Ident>,
pub ident: proc_macro2::Ident,
pub val: syn::Expr,
}

impl Parse for DefaultParam {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
if input.peek(syn::Ident) {
let ident: Option<proc_macro2::Ident> = 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::<proc_macro2::Ident>()
.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<syn::Expr>,
min: Option<syn::Expr>,
max: Option<syn::Expr>,
) -> [Option<syn::Expr>; 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<String, String>) -> 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);

// }
68 changes: 0 additions & 68 deletions crates/fj-proc/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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<String> = 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<String, String>) -> 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<String, String>) -> 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);

// }
3 changes: 3 additions & 0 deletions crates/fj/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,6 @@ serialization = ["serde"]

[dependencies]
serde = { version = "1.0.7", optional = true }

[dependencies.fj-proc]
path = "../../crates/fj-proc"
1 change: 1 addition & 0 deletions crates/fj/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
3 changes: 0 additions & 3 deletions models/star/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,3 @@ crate-type = ["cdylib"]

[dependencies.fj]
path = "../../crates/fj"

[dependencies.fj-proc]
path = "../../crates/fj-proc"
Loading