Skip to content

Commit

Permalink
Move the logic for the entry macro into entry.rs
Browse files Browse the repository at this point in the history
Replace with a stub in `lib.rs`
  • Loading branch information
speelbarrow committed Oct 24, 2023
1 parent cfba466 commit 438a0ea
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 102 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# CHANGELOG

### [unreleased](https://github.com/speelbarrow/weensy.rs/blob/main)
- Move the logic for the `entry` macro into [`entry.rs`
](https://github.com/speelbarrow/weensy.rs/blob/proc-macro/src/entry.rs#L12), replace with a stub in [`lib.rs`
](https://github.com/speelbarrow/weensy.rs/blob/proc-macro/src/lib.rs#L45)

### [v0.1.2](https://github.com/speelbarrow/weensy.rs/blob/v0.1.2)
- Fix [verification of function signature by `entry` attribute
macro](https://github.com/speelbarrow/weensy.rs/blob/v0.1.2/proc-macro/src/lib.rs#L88)
Expand Down
107 changes: 101 additions & 6 deletions proc-macro/src/entry.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,115 @@
use proc_macro2::Ident;
use proc_macro::TokenStream;
use proc_macro2::{Ident as Ident2, TokenStream as TokenStream2};
use quote::{quote, ToTokens};
use syn::{
bracketed,
parse::{Parse, ParseStream},
parse_quote,
parse_macro_input, parse_quote,
punctuated::Punctuated,
LitInt, Pat, PatIdent, Result, Token,
ItemFn, LitInt, Pat, PatIdent, Result as SynResult, ReturnType, Signature, Token, Type,
};

pub fn entry(attr: TokenStream, input: TokenStream) -> TokenStream {
let ItemFn {
attrs,
vis,
sig,
block,
} = parse_macro_input!(input as ItemFn);
if let Err(e) = verify_signature(&sig) {
return e.into();
};

let resources = {
if attr.is_empty() {
None
} else {
Some(parse_macro_input!(attr with Punctuated::<Arg, Token![,]>::parse_separated_nonempty)
.into_iter())
}
}.map(|args| {
quote! {
let teensy4_bsp::board::Resources {
#(#args),* , ..
} = teensy4_bsp::board::t41(teensy4_bsp::board::instances());
}
});

quote! {
#[no_mangle]
#(#attrs)*
#vis #sig {
#resources

#block
}
}
.into()
}

fn verify_signature(signature: &Signature) -> Result<(), TokenStream2> {
macro_rules! is {
(@some $( $prop: ident ).+, $value: expr) => {
is!(@eq $( $prop ).+, Some($value))
};
(@none $( $prop: ident ).+) => {
is!(@eq $( $prop ).+, None)
};
(@eq $( $prop: ident ).+, $value: expr) => {
is!( signature.$( $prop ).+ != $value, &signature.$( $prop ).+ )
};
(@ne $( $prop: ident ).+, $value: expr) => {
is!( signature.$( $prop ).+ == $value, &signature.$( $prop ).+ );
};
(@empty $( $prop: ident ).+) => {
is!( !signature.$( $prop ).+.is_empty(), &signature.$( $prop ).+ );
};
(@matches $lhs: pat, $( $prop: ident ).+, $out: expr) => {
if let $lhs = $( $prop ).+ {
is!(@err $out);
}
};
($bool: expr, $out: expr) => {
if $bool {
is!(@err $out);
}
};
(@err $out: expr) => {
return Err(syn::Error::new_spanned(
$out,
"this attribute may only be applied to a function with the signature `fn() main -> !`",
).to_compile_error());
};
}

is!(@none constness);
is!(@none asyncness);
is!(@none unsafety);
is!(@none abi);
is!(@eq ident, "main");
is!(@none generics.lt_token);
is!(@none generics.gt_token);
is!(@empty generics.params);
is!(@none generics.where_clause);
is!(@empty inputs);
is!(@none variadic);

if let ReturnType::Type(_, boxed) = &signature.output {
if let Type::Never(_) = **boxed {
return Ok(());
}
}

is!(@err &signature.output);
}

pub enum Arg {
Ident(PatIdent),
Pins(Punctuated<PatIdent, Token![,]>),
}

impl Parse for Arg {
fn parse(input: ParseStream) -> Result<Self> {
fn parse(input: ParseStream) -> SynResult<Self> {
if let Ok(Pat::Ident(ident)) = Pat::parse_single(input) {
return if ident.ident == "pins" {
let content;
Expand All @@ -30,10 +125,10 @@ impl Parse for Arg {
}
}

fn parse_pin(input: ParseStream) -> Result<PatIdent> {
fn parse_pin(input: ParseStream) -> SynResult<PatIdent> {
let mutability = input.parse::<Token![mut]>().ok();
let num = input.parse::<LitInt>()?;
let ident = Ident::new(&("p".to_owned() + num.base10_digits()), num.span());
let ident = Ident2::new(&("p".to_owned() + num.base10_digits()), num.span());
if let Pat::Ident(r) = parse_quote!(#mutability #ident) {
Ok(r)
} else {
Expand Down
97 changes: 1 addition & 96 deletions proc-macro/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::{parse_macro_input, punctuated::Punctuated, ItemFn, ReturnType, Signature, Token, Type};

mod entry;
use entry::Arg;

/**
Configures your `main` function to be exported properly.
Expand Down Expand Up @@ -48,95 +43,5 @@ fn main() {
*/
#[proc_macro_attribute]
pub fn entry(attr: TokenStream, input: TokenStream) -> TokenStream {
let ItemFn {
attrs,
vis,
sig,
block,
} = parse_macro_input!(input as ItemFn);
if let Err(e) = verify_signature(&sig) {
return e.into();
};

let resources = {
if attr.is_empty() {
None
} else {
Some(parse_macro_input!(attr with Punctuated::<Arg, Token![,]>::parse_separated_nonempty)
.into_iter())
}
}.map(|args| {
quote! {
let teensy4_bsp::board::Resources {
#(#args),* , ..
} = teensy4_bsp::board::t41(teensy4_bsp::board::instances());
}
});

quote! {
#[no_mangle]
#(#attrs)*
#vis #sig {
#resources

#block
}
}
.into()
}

fn verify_signature(signature: &Signature) -> Result<(), TokenStream2> {
macro_rules! is {
(@some $( $prop: ident ).+, $value: expr) => {
is!(@eq $( $prop ).+, Some($value))
};
(@none $( $prop: ident ).+) => {
is!(@eq $( $prop ).+, None)
};
(@eq $( $prop: ident ).+, $value: expr) => {
is!( signature.$( $prop ).+ != $value, &signature.$( $prop ).+ )
};
(@ne $( $prop: ident ).+, $value: expr) => {
is!( signature.$( $prop ).+ == $value, &signature.$( $prop ).+ );
};
(@empty $( $prop: ident ).+) => {
is!( !signature.$( $prop ).+.is_empty(), &signature.$( $prop ).+ );
};
(@matches $lhs: pat, $( $prop: ident ).+, $out: expr) => {
if let $lhs = $( $prop ).+ {
is!(@err $out);
}
};
($bool: expr, $out: expr) => {
if $bool {
is!(@err $out);
}
};
(@err $out: expr) => {
return Err(syn::Error::new_spanned(
$out,
"this attribute may only be applied to a function with the signature `fn() main -> !`",
).to_compile_error());
};
}

is!(@none constness);
is!(@none asyncness);
is!(@none unsafety);
is!(@none abi);
is!(@eq ident, "main");
is!(@none generics.lt_token);
is!(@none generics.gt_token);
is!(@empty generics.params);
is!(@none generics.where_clause);
is!(@empty inputs);
is!(@none variadic);

if let ReturnType::Type(_, boxed) = &signature.output {
if let Type::Never(_) = **boxed {
return Ok(());
}
}

is!(@err &signature.output);
entry::entry(attr, input)
}

0 comments on commit 438a0ea

Please sign in to comment.