implement handling #[cfg] and #[cfg_attr] in proc macro inputs
This commit is contained in:
parent
2ab8428062
commit
7005fa3330
|
@ -3,14 +3,20 @@
|
||||||
#![cfg_attr(test, recursion_limit = "512")]
|
#![cfg_attr(test, recursion_limit = "512")]
|
||||||
use proc_macro2::{Span, TokenStream};
|
use proc_macro2::{Span, TokenStream};
|
||||||
use quote::{quote, ToTokens};
|
use quote::{quote, ToTokens};
|
||||||
use std::io::{ErrorKind, Write};
|
use std::{
|
||||||
|
collections::{hash_map::Entry, HashMap},
|
||||||
|
io::{ErrorKind, Write},
|
||||||
|
};
|
||||||
use syn::{
|
use syn::{
|
||||||
bracketed, parenthesized,
|
bracketed,
|
||||||
|
ext::IdentExt,
|
||||||
|
parenthesized,
|
||||||
parse::{Parse, ParseStream, Parser},
|
parse::{Parse, ParseStream, Parser},
|
||||||
parse_quote,
|
parse_quote,
|
||||||
punctuated::Pair,
|
punctuated::{Pair, Punctuated},
|
||||||
spanned::Spanned,
|
spanned::Spanned,
|
||||||
AttrStyle, Attribute, Error, Item, ItemFn, Token,
|
token::{Bracket, Paren},
|
||||||
|
AttrStyle, Attribute, Error, Ident, Item, ItemFn, LitBool, LitStr, Meta, Token,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod fold;
|
mod fold;
|
||||||
|
@ -19,6 +25,7 @@ mod hdl_enum;
|
||||||
mod hdl_type_alias;
|
mod hdl_type_alias;
|
||||||
mod hdl_type_common;
|
mod hdl_type_common;
|
||||||
mod module;
|
mod module;
|
||||||
|
mod process_cfg;
|
||||||
|
|
||||||
pub(crate) trait CustomToken:
|
pub(crate) trait CustomToken:
|
||||||
Copy
|
Copy
|
||||||
|
@ -59,6 +66,11 @@ mod kw {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
custom_keyword!(__evaluated_cfgs);
|
||||||
|
custom_keyword!(all);
|
||||||
|
custom_keyword!(any);
|
||||||
|
custom_keyword!(cfg);
|
||||||
|
custom_keyword!(cfg_attr);
|
||||||
custom_keyword!(clock_domain);
|
custom_keyword!(clock_domain);
|
||||||
custom_keyword!(connect_inexact);
|
custom_keyword!(connect_inexact);
|
||||||
custom_keyword!(custom_bounds);
|
custom_keyword!(custom_bounds);
|
||||||
|
@ -75,6 +87,7 @@ mod kw {
|
||||||
custom_keyword!(no_reset);
|
custom_keyword!(no_reset);
|
||||||
custom_keyword!(no_runtime_generics);
|
custom_keyword!(no_runtime_generics);
|
||||||
custom_keyword!(no_static);
|
custom_keyword!(no_static);
|
||||||
|
custom_keyword!(not);
|
||||||
custom_keyword!(outline_generated);
|
custom_keyword!(outline_generated);
|
||||||
custom_keyword!(output);
|
custom_keyword!(output);
|
||||||
custom_keyword!(reg_builder);
|
custom_keyword!(reg_builder);
|
||||||
|
@ -901,15 +914,335 @@ fn hdl_module_impl(item: ItemFn) -> syn::Result<TokenStream> {
|
||||||
Ok(contents)
|
Ok(contents)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hdl_module(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> {
|
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
let kw = kw::hdl_module::default();
|
pub(crate) enum CfgExpr {
|
||||||
hdl_module_impl(syn::parse2(quote! { #[#kw(#attr)] #item })?)
|
Option {
|
||||||
|
ident: Ident,
|
||||||
|
value: Option<(Token![=], LitStr)>,
|
||||||
|
},
|
||||||
|
All {
|
||||||
|
all: kw::all,
|
||||||
|
paren: Paren,
|
||||||
|
exprs: Punctuated<CfgExpr, Token![,]>,
|
||||||
|
},
|
||||||
|
Any {
|
||||||
|
any: kw::any,
|
||||||
|
paren: Paren,
|
||||||
|
exprs: Punctuated<CfgExpr, Token![,]>,
|
||||||
|
},
|
||||||
|
Not {
|
||||||
|
not: kw::not,
|
||||||
|
paren: Paren,
|
||||||
|
expr: Box<CfgExpr>,
|
||||||
|
trailing_comma: Option<Token![,]>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hdl_attr(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> {
|
impl Parse for CfgExpr {
|
||||||
let kw = kw::hdl::default();
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
let item = quote! { #[#kw(#attr)] #item };
|
match input.cursor().ident() {
|
||||||
let item = syn::parse2::<Item>(item)?;
|
Some((_, cursor)) if cursor.eof() => {
|
||||||
|
return Ok(CfgExpr::Option {
|
||||||
|
ident: input.call(Ident::parse_any)?,
|
||||||
|
value: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
if input.peek(Ident::peek_any) && input.peek2(Token![=]) {
|
||||||
|
return Ok(CfgExpr::Option {
|
||||||
|
ident: input.call(Ident::parse_any)?,
|
||||||
|
value: Some((input.parse()?, input.parse()?)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let contents;
|
||||||
|
if input.peek(kw::all) {
|
||||||
|
Ok(CfgExpr::All {
|
||||||
|
all: input.parse()?,
|
||||||
|
paren: parenthesized!(contents in input),
|
||||||
|
exprs: contents.call(Punctuated::parse_terminated)?,
|
||||||
|
})
|
||||||
|
} else if input.peek(kw::any) {
|
||||||
|
Ok(CfgExpr::Any {
|
||||||
|
any: input.parse()?,
|
||||||
|
paren: parenthesized!(contents in input),
|
||||||
|
exprs: contents.call(Punctuated::parse_terminated)?,
|
||||||
|
})
|
||||||
|
} else if input.peek(kw::not) {
|
||||||
|
Ok(CfgExpr::Not {
|
||||||
|
not: input.parse()?,
|
||||||
|
paren: parenthesized!(contents in input),
|
||||||
|
expr: contents.parse()?,
|
||||||
|
trailing_comma: contents.parse()?,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(input.error("expected cfg-pattern"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToTokens for CfgExpr {
|
||||||
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
|
match self {
|
||||||
|
CfgExpr::Option { ident, value } => {
|
||||||
|
ident.to_tokens(tokens);
|
||||||
|
if let Some((eq, value)) = value {
|
||||||
|
eq.to_tokens(tokens);
|
||||||
|
value.to_tokens(tokens);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CfgExpr::All { all, paren, exprs } => {
|
||||||
|
all.to_tokens(tokens);
|
||||||
|
paren.surround(tokens, |tokens| exprs.to_tokens(tokens));
|
||||||
|
}
|
||||||
|
CfgExpr::Any { any, paren, exprs } => {
|
||||||
|
any.to_tokens(tokens);
|
||||||
|
paren.surround(tokens, |tokens| exprs.to_tokens(tokens));
|
||||||
|
}
|
||||||
|
CfgExpr::Not {
|
||||||
|
not,
|
||||||
|
paren,
|
||||||
|
expr,
|
||||||
|
trailing_comma,
|
||||||
|
} => {
|
||||||
|
not.to_tokens(tokens);
|
||||||
|
paren.surround(tokens, |tokens| {
|
||||||
|
expr.to_tokens(tokens);
|
||||||
|
trailing_comma.to_tokens(tokens);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
|
pub(crate) struct Cfg {
|
||||||
|
cfg: kw::cfg,
|
||||||
|
paren: Paren,
|
||||||
|
expr: CfgExpr,
|
||||||
|
trailing_comma: Option<Token![,]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cfg {
|
||||||
|
fn parse_meta(meta: &Meta) -> syn::Result<Self> {
|
||||||
|
syn::parse2(meta.to_token_stream())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToTokens for Cfg {
|
||||||
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
|
let Self {
|
||||||
|
cfg,
|
||||||
|
paren,
|
||||||
|
expr,
|
||||||
|
trailing_comma,
|
||||||
|
} = self;
|
||||||
|
cfg.to_tokens(tokens);
|
||||||
|
paren.surround(tokens, |tokens| {
|
||||||
|
expr.to_tokens(tokens);
|
||||||
|
trailing_comma.to_tokens(tokens);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for Cfg {
|
||||||
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
|
let contents;
|
||||||
|
Ok(Self {
|
||||||
|
cfg: input.parse()?,
|
||||||
|
paren: parenthesized!(contents in input),
|
||||||
|
expr: contents.parse()?,
|
||||||
|
trailing_comma: contents.parse()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
|
pub(crate) struct CfgAttr {
|
||||||
|
cfg_attr: kw::cfg_attr,
|
||||||
|
paren: Paren,
|
||||||
|
expr: CfgExpr,
|
||||||
|
comma: Token![,],
|
||||||
|
attrs: Punctuated<Meta, Token![,]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CfgAttr {
|
||||||
|
pub(crate) fn to_cfg(&self) -> Cfg {
|
||||||
|
Cfg {
|
||||||
|
cfg: kw::cfg(self.cfg_attr.span),
|
||||||
|
paren: self.paren,
|
||||||
|
expr: self.expr.clone(),
|
||||||
|
trailing_comma: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn parse_meta(meta: &Meta) -> syn::Result<Self> {
|
||||||
|
syn::parse2(meta.to_token_stream())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for CfgAttr {
|
||||||
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
|
let contents;
|
||||||
|
Ok(Self {
|
||||||
|
cfg_attr: input.parse()?,
|
||||||
|
paren: parenthesized!(contents in input),
|
||||||
|
expr: contents.parse()?,
|
||||||
|
comma: contents.parse()?,
|
||||||
|
attrs: contents.call(Punctuated::parse_terminated)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct CfgAndValue {
|
||||||
|
cfg: Cfg,
|
||||||
|
eq_token: Token![=],
|
||||||
|
value: LitBool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for CfgAndValue {
|
||||||
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
|
Ok(Self {
|
||||||
|
cfg: input.parse()?,
|
||||||
|
eq_token: input.parse()?,
|
||||||
|
value: input.parse()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct Cfgs<T> {
|
||||||
|
pub(crate) bracket: Bracket,
|
||||||
|
pub(crate) cfgs_map: HashMap<Cfg, T>,
|
||||||
|
pub(crate) cfgs_list: Vec<Cfg>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Default for Cfgs<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
bracket: Default::default(),
|
||||||
|
cfgs_map: Default::default(),
|
||||||
|
cfgs_list: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Cfgs<T> {
|
||||||
|
fn insert_cfg(&mut self, cfg: Cfg, value: T) {
|
||||||
|
match self.cfgs_map.entry(cfg) {
|
||||||
|
Entry::Occupied(_) => {}
|
||||||
|
Entry::Vacant(entry) => {
|
||||||
|
self.cfgs_list.push(entry.key().clone());
|
||||||
|
entry.insert(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for Cfgs<bool> {
|
||||||
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
|
let contents;
|
||||||
|
let bracket = bracketed!(contents in input);
|
||||||
|
let mut cfgs_map = HashMap::new();
|
||||||
|
let mut cfgs_list = Vec::new();
|
||||||
|
for CfgAndValue {
|
||||||
|
cfg,
|
||||||
|
eq_token,
|
||||||
|
value,
|
||||||
|
} in contents.call(Punctuated::<CfgAndValue, Token![,]>::parse_terminated)?
|
||||||
|
{
|
||||||
|
let _ = eq_token;
|
||||||
|
match cfgs_map.entry(cfg) {
|
||||||
|
Entry::Occupied(_) => {}
|
||||||
|
Entry::Vacant(entry) => {
|
||||||
|
cfgs_list.push(entry.key().clone());
|
||||||
|
entry.insert(value.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Self {
|
||||||
|
bracket,
|
||||||
|
cfgs_map,
|
||||||
|
cfgs_list,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for Cfgs<()> {
|
||||||
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
|
let contents;
|
||||||
|
let bracket = bracketed!(contents in input);
|
||||||
|
let mut cfgs_map = HashMap::new();
|
||||||
|
let mut cfgs_list = Vec::new();
|
||||||
|
for cfg in contents.call(Punctuated::<Cfg, Token![,]>::parse_terminated)? {
|
||||||
|
match cfgs_map.entry(cfg) {
|
||||||
|
Entry::Occupied(_) => {}
|
||||||
|
Entry::Vacant(entry) => {
|
||||||
|
cfgs_list.push(entry.key().clone());
|
||||||
|
entry.insert(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Self {
|
||||||
|
bracket,
|
||||||
|
cfgs_map,
|
||||||
|
cfgs_list,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToTokens for Cfgs<()> {
|
||||||
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
|
let Self {
|
||||||
|
bracket,
|
||||||
|
cfgs_map: _,
|
||||||
|
cfgs_list,
|
||||||
|
} = self;
|
||||||
|
bracket.surround(tokens, |tokens| {
|
||||||
|
for cfg in cfgs_list {
|
||||||
|
cfg.to_tokens(tokens);
|
||||||
|
<Token![,]>::default().to_tokens(tokens);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hdl_main(
|
||||||
|
kw: impl CustomToken,
|
||||||
|
attr: TokenStream,
|
||||||
|
item: TokenStream,
|
||||||
|
) -> syn::Result<TokenStream> {
|
||||||
|
let (evaluated_cfgs, attr): (_, TokenStream) = Parser::parse2(
|
||||||
|
|input: ParseStream| {
|
||||||
|
if input.peek(Bracket) && input.peek2(kw::__evaluated_cfgs) {
|
||||||
|
let cfgs = input.parse()?;
|
||||||
|
let _: kw::__evaluated_cfgs = input.parse()?;
|
||||||
|
Ok((Some(cfgs), input.parse()?))
|
||||||
|
} else {
|
||||||
|
Ok((None, input.parse()?))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
attr,
|
||||||
|
)?;
|
||||||
|
let cfgs = if let Some(cfgs) = evaluated_cfgs {
|
||||||
|
cfgs
|
||||||
|
} else {
|
||||||
|
let cfgs = process_cfg::collect_cfgs(syn::parse2(item.clone())?)?;
|
||||||
|
if cfgs.cfgs_list.is_empty() {
|
||||||
|
Cfgs::default()
|
||||||
|
} else {
|
||||||
|
let key = kw::__evaluated_cfgs::default();
|
||||||
|
return Ok(quote! {
|
||||||
|
::fayalite::__cfg_expansion_helper! {
|
||||||
|
[]
|
||||||
|
#cfgs
|
||||||
|
#[::fayalite::#kw:(#key #attr)] { #item }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let item = syn::parse2(quote! { #[#kw(#attr)] #item })?;
|
||||||
|
let Some(item) = process_cfg::process_cfgs(item, cfgs)? else {
|
||||||
|
return Ok(TokenStream::new());
|
||||||
|
};
|
||||||
match item {
|
match item {
|
||||||
Item::Enum(item) => hdl_enum::hdl_enum(item),
|
Item::Enum(item) => hdl_enum::hdl_enum(item),
|
||||||
Item::Struct(item) => hdl_bundle::hdl_bundle(item),
|
Item::Struct(item) => hdl_bundle::hdl_bundle(item),
|
||||||
|
@ -921,3 +1254,11 @@ pub fn hdl_attr(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn hdl_module(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> {
|
||||||
|
hdl_main(kw::hdl_module::default(), attr, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hdl_attr(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> {
|
||||||
|
hdl_main(kw::hdl::default(), attr, item)
|
||||||
|
}
|
||||||
|
|
2527
crates/fayalite-proc-macros-impl/src/process_cfg.rs
Normal file
2527
crates/fayalite-proc-macros-impl/src/process_cfg.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -11,6 +11,56 @@ extern crate self as fayalite;
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub use std as __std;
|
pub use std as __std;
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! __cfg_expansion_helper {
|
||||||
|
(
|
||||||
|
[
|
||||||
|
$($evaluated_cfgs:ident($($evaluated_exprs:tt)*) = $evaluated_results:ident,)*
|
||||||
|
]
|
||||||
|
[
|
||||||
|
$cfg:ident($($expr:tt)*),
|
||||||
|
$($unevaluated_cfgs:ident($($unevaluated_exprs:tt)*),)*
|
||||||
|
]
|
||||||
|
#[$after_evaluation:path:($($after_evaluation_attr_args:tt)*)] {$($after_evaluation_args:tt)*}
|
||||||
|
) => {
|
||||||
|
#[$cfg($($expr)*)]
|
||||||
|
$crate::__cfg_expansion_helper! {
|
||||||
|
[
|
||||||
|
$($evaluated_cfgs($($evaluated_exprs)*) = $evaluated_results,)*
|
||||||
|
$cfg($($expr)*) = true,
|
||||||
|
]
|
||||||
|
[
|
||||||
|
$($unevaluated_cfgs($($unevaluated_exprs)*),)*
|
||||||
|
]
|
||||||
|
#[$after_evaluation:($($after_evaluation_attr_args)*)] {$($after_evaluation_args)*}
|
||||||
|
}
|
||||||
|
#[$cfg(not($($expr)*))]
|
||||||
|
$crate::__cfg_expansion_helper! {
|
||||||
|
[
|
||||||
|
$($evaluated_cfgs($($evaluated_exprs)*) = $evaluated_results,)*
|
||||||
|
$cfg($($expr)*) = false,
|
||||||
|
]
|
||||||
|
[
|
||||||
|
$($unevaluated_cfgs($($unevaluated_exprs)*),)*
|
||||||
|
]
|
||||||
|
#[$after_evaluation:($($after_evaluation_attr_args)*)] {$($after_evaluation_args)*}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
(
|
||||||
|
[
|
||||||
|
$($evaluated_cfgs:ident($($evaluated_exprs:tt)*) = $evaluated_results:ident,)*
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
#[$after_evaluation:path:($($after_evaluation_attr_args:tt)*)] {$($after_evaluation_args:tt)*}
|
||||||
|
) => {
|
||||||
|
#[$after_evaluation([
|
||||||
|
$($evaluated_cfgs($($evaluated_exprs)*) = $evaluated_results,)*
|
||||||
|
] $($after_evaluation_attr_args)*)]
|
||||||
|
$($after_evaluation_args)*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
/// The `#[hdl_module]` attribute is applied to a Rust function so that that function creates
|
/// The `#[hdl_module]` attribute is applied to a Rust function so that that function creates
|
||||||
/// a [`Module`][`::fayalite::module::Module`] when called.
|
/// a [`Module`][`::fayalite::module::Module`] when called.
|
||||||
|
|
Loading…
Reference in a new issue