forked from libre-chip/fayalite
		
	
		
			
				
	
	
		
			1277 lines
		
	
	
	
		
			37 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			1277 lines
		
	
	
	
		
			37 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | |
| // See Notices.txt for copyright information
 | |
| #![cfg_attr(test, recursion_limit = "512")]
 | |
| use proc_macro2::{Span, TokenStream};
 | |
| use quote::{ToTokens, quote};
 | |
| use std::{
 | |
|     collections::{HashMap, hash_map::Entry},
 | |
|     io::{ErrorKind, Write},
 | |
| };
 | |
| use syn::{
 | |
|     AttrStyle, Attribute, Error, Ident, Item, ItemFn, LitBool, LitStr, Meta, Token, bracketed,
 | |
|     ext::IdentExt,
 | |
|     parenthesized,
 | |
|     parse::{Parse, ParseStream, Parser},
 | |
|     parse_quote,
 | |
|     punctuated::{Pair, Punctuated},
 | |
|     spanned::Spanned,
 | |
|     token::{Bracket, Paren},
 | |
| };
 | |
| 
 | |
| mod fold;
 | |
| mod hdl_bundle;
 | |
| mod hdl_enum;
 | |
| mod hdl_type_alias;
 | |
| mod hdl_type_common;
 | |
| mod module;
 | |
| mod process_cfg;
 | |
| 
 | |
| pub(crate) trait CustomToken:
 | |
|     Copy
 | |
|     + Spanned
 | |
|     + ToTokens
 | |
|     + std::fmt::Debug
 | |
|     + Eq
 | |
|     + std::hash::Hash
 | |
|     + Default
 | |
|     + quote::IdentFragment
 | |
|     + Parse
 | |
| {
 | |
|     const IDENT_STR: &'static str;
 | |
| }
 | |
| 
 | |
| mod kw {
 | |
|     pub(crate) use syn::token::Extern as extern_;
 | |
| 
 | |
|     macro_rules! custom_keyword {
 | |
|         ($kw:ident) => {
 | |
|             syn::custom_keyword!($kw);
 | |
| 
 | |
|             impl quote::IdentFragment for $kw {
 | |
|                 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
 | |
|                     f.write_str(stringify!($kw))
 | |
|                 }
 | |
| 
 | |
|                 fn span(&self) -> Option<proc_macro2::Span> {
 | |
|                     Some(self.span)
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             crate::fold::no_op_fold!($kw);
 | |
| 
 | |
|             impl crate::CustomToken for $kw {
 | |
|                 const IDENT_STR: &'static str = stringify!($kw);
 | |
|             }
 | |
|         };
 | |
|     }
 | |
| 
 | |
|     custom_keyword!(__evaluated_cfgs);
 | |
|     custom_keyword!(add_platform_io);
 | |
|     custom_keyword!(all);
 | |
|     custom_keyword!(any);
 | |
|     custom_keyword!(cfg);
 | |
|     custom_keyword!(cfg_attr);
 | |
|     custom_keyword!(clock_domain);
 | |
|     custom_keyword!(cmp_eq);
 | |
|     custom_keyword!(connect_inexact);
 | |
|     custom_keyword!(custom_bounds);
 | |
|     custom_keyword!(flip);
 | |
|     custom_keyword!(hdl);
 | |
|     custom_keyword!(hdl_module);
 | |
|     custom_keyword!(incomplete_wire);
 | |
|     custom_keyword!(input);
 | |
|     custom_keyword!(instance);
 | |
|     custom_keyword!(m);
 | |
|     custom_keyword!(memory);
 | |
|     custom_keyword!(memory_array);
 | |
|     custom_keyword!(memory_with_init);
 | |
|     custom_keyword!(no_reset);
 | |
|     custom_keyword!(no_runtime_generics);
 | |
|     custom_keyword!(no_static);
 | |
|     custom_keyword!(not);
 | |
|     custom_keyword!(outline_generated);
 | |
|     custom_keyword!(output);
 | |
|     custom_keyword!(reg_builder);
 | |
|     custom_keyword!(reset);
 | |
|     custom_keyword!(sim);
 | |
|     custom_keyword!(skip);
 | |
|     custom_keyword!(target);
 | |
|     custom_keyword!(wire);
 | |
| }
 | |
| 
 | |
| type Pound = Token![#]; // work around https://github.com/rust-lang/rust/issues/50676
 | |
| 
 | |
| #[derive(Clone, Debug)]
 | |
| pub(crate) struct HdlAttr<T, KW> {
 | |
|     pub(crate) pound_token: Pound,
 | |
|     pub(crate) style: AttrStyle,
 | |
|     pub(crate) bracket_token: syn::token::Bracket,
 | |
|     pub(crate) kw: KW,
 | |
|     pub(crate) paren_token: Option<syn::token::Paren>,
 | |
|     pub(crate) body: T,
 | |
| }
 | |
| 
 | |
| crate::fold::impl_fold! {
 | |
|     struct HdlAttr<T, KW,> {
 | |
|         pound_token: Pound,
 | |
|         style: AttrStyle,
 | |
|         bracket_token: syn::token::Bracket,
 | |
|         kw: KW,
 | |
|         paren_token: Option<syn::token::Paren>,
 | |
|         body: T,
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[allow(dead_code)]
 | |
| impl<T, KW> HdlAttr<T, KW> {
 | |
|     pub(crate) fn split_body(self) -> (HdlAttr<(), KW>, T) {
 | |
|         let Self {
 | |
|             pound_token,
 | |
|             style,
 | |
|             bracket_token,
 | |
|             kw,
 | |
|             paren_token,
 | |
|             body,
 | |
|         } = self;
 | |
|         (
 | |
|             HdlAttr {
 | |
|                 pound_token,
 | |
|                 style,
 | |
|                 bracket_token,
 | |
|                 kw,
 | |
|                 paren_token,
 | |
|                 body: (),
 | |
|             },
 | |
|             body,
 | |
|         )
 | |
|     }
 | |
|     pub(crate) fn replace_body<T2>(self, body: T2) -> HdlAttr<T2, KW> {
 | |
|         let Self {
 | |
|             pound_token,
 | |
|             style,
 | |
|             bracket_token,
 | |
|             kw,
 | |
|             paren_token,
 | |
|             body: _,
 | |
|         } = self;
 | |
|         HdlAttr {
 | |
|             pound_token,
 | |
|             style,
 | |
|             bracket_token,
 | |
|             kw,
 | |
|             paren_token,
 | |
|             body,
 | |
|         }
 | |
|     }
 | |
|     pub(crate) fn as_ref(&self) -> HdlAttr<&T, KW>
 | |
|     where
 | |
|         KW: Clone,
 | |
|     {
 | |
|         let Self {
 | |
|             pound_token,
 | |
|             style,
 | |
|             bracket_token,
 | |
|             ref kw,
 | |
|             paren_token,
 | |
|             ref body,
 | |
|         } = *self;
 | |
|         HdlAttr {
 | |
|             pound_token,
 | |
|             style,
 | |
|             bracket_token,
 | |
|             kw: kw.clone(),
 | |
|             paren_token,
 | |
|             body,
 | |
|         }
 | |
|     }
 | |
|     pub(crate) fn try_map<R, E, F: FnOnce(T) -> Result<R, E>>(
 | |
|         self,
 | |
|         f: F,
 | |
|     ) -> Result<HdlAttr<R, KW>, E> {
 | |
|         let Self {
 | |
|             pound_token,
 | |
|             style,
 | |
|             bracket_token,
 | |
|             kw,
 | |
|             paren_token,
 | |
|             body,
 | |
|         } = self;
 | |
|         Ok(HdlAttr {
 | |
|             pound_token,
 | |
|             style,
 | |
|             bracket_token,
 | |
|             kw,
 | |
|             paren_token,
 | |
|             body: f(body)?,
 | |
|         })
 | |
|     }
 | |
|     pub(crate) fn map<R, F: FnOnce(T) -> R>(self, f: F) -> HdlAttr<R, KW> {
 | |
|         let Self {
 | |
|             pound_token,
 | |
|             style,
 | |
|             bracket_token,
 | |
|             kw,
 | |
|             paren_token,
 | |
|             body,
 | |
|         } = self;
 | |
|         HdlAttr {
 | |
|             pound_token,
 | |
|             style,
 | |
|             bracket_token,
 | |
|             kw,
 | |
|             paren_token,
 | |
|             body: f(body),
 | |
|         }
 | |
|     }
 | |
|     fn to_attr(&self) -> Attribute
 | |
|     where
 | |
|         T: ToTokens,
 | |
|         KW: ToTokens,
 | |
|     {
 | |
|         parse_quote! { #self }
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl<T: Default, KW: Default> Default for HdlAttr<T, KW> {
 | |
|     fn default() -> Self {
 | |
|         T::default().into()
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl<T, KW: Default> From<T> for HdlAttr<T, KW> {
 | |
|     fn from(body: T) -> Self {
 | |
|         HdlAttr {
 | |
|             pound_token: Default::default(),
 | |
|             style: AttrStyle::Outer,
 | |
|             bracket_token: Default::default(),
 | |
|             kw: Default::default(),
 | |
|             paren_token: Default::default(),
 | |
|             body,
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl<T: ToTokens, KW: ToTokens + Spanned> ToTokens for HdlAttr<T, KW> {
 | |
|     fn to_tokens(&self, tokens: &mut TokenStream) {
 | |
|         self.pound_token.to_tokens(tokens);
 | |
|         match self.style {
 | |
|             AttrStyle::Inner(style) => style.to_tokens(tokens),
 | |
|             AttrStyle::Outer => {}
 | |
|         };
 | |
|         self.bracket_token.surround(tokens, |tokens| {
 | |
|             self.kw.to_tokens(tokens);
 | |
|             match self.paren_token {
 | |
|                 Some(paren_token) => {
 | |
|                     paren_token.surround(tokens, |tokens| self.body.to_tokens(tokens))
 | |
|                 }
 | |
|                 None => {
 | |
|                     let body = self.body.to_token_stream();
 | |
|                     if !body.is_empty() {
 | |
|                         syn::token::Paren(self.kw.span())
 | |
|                             .surround(tokens, |tokens| tokens.extend([body]));
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         });
 | |
|     }
 | |
| }
 | |
| 
 | |
| fn is_hdl_attr<KW: CustomToken>(attr: &Attribute) -> bool {
 | |
|     attr.path().is_ident(KW::IDENT_STR)
 | |
| }
 | |
| 
 | |
| impl<T: Parse, KW: Parse> HdlAttr<T, KW> {
 | |
|     fn parse_and_take_attr(attrs: &mut Vec<Attribute>) -> syn::Result<Option<Self>>
 | |
|     where
 | |
|         KW: ToTokens,
 | |
|     {
 | |
|         let mut retval = None;
 | |
|         let mut errors = Errors::new();
 | |
|         attrs.retain(|attr| {
 | |
|             if let Ok(kw) = syn::parse2::<KW>(attr.path().to_token_stream()) {
 | |
|                 if retval.is_some() {
 | |
|                     errors.push(Error::new_spanned(
 | |
|                         attr,
 | |
|                         format_args!("more than one #[{}] attribute", kw.to_token_stream()),
 | |
|                     ));
 | |
|                 }
 | |
|                 errors.unwrap_or_default(Self::parse_attr(attr).map(|v| retval = Some(v)));
 | |
|                 false
 | |
|             } else {
 | |
|                 true
 | |
|             }
 | |
|         });
 | |
|         errors.finish()?;
 | |
|         Ok(retval)
 | |
|     }
 | |
|     fn parse_and_leave_attr(attrs: &[Attribute]) -> syn::Result<Option<Self>>
 | |
|     where
 | |
|         KW: ToTokens,
 | |
|     {
 | |
|         let mut retval = None;
 | |
|         let mut errors = Errors::new();
 | |
|         for attr in attrs {
 | |
|             if let Ok(kw) = syn::parse2::<KW>(attr.path().to_token_stream()) {
 | |
|                 if retval.is_some() {
 | |
|                     errors.push(Error::new_spanned(
 | |
|                         attr,
 | |
|                         format_args!("more than one #[{}] attribute", kw.to_token_stream()),
 | |
|                     ));
 | |
|                 }
 | |
|                 errors.unwrap_or_default(Self::parse_attr(attr).map(|v| retval = Some(v)));
 | |
|             }
 | |
|         }
 | |
|         errors.finish()?;
 | |
|         Ok(retval)
 | |
|     }
 | |
|     fn parse_attr(attr: &Attribute) -> syn::Result<Self> {
 | |
|         match attr.style {
 | |
|             AttrStyle::Outer => Parser::parse2(Self::parse_outer, attr.to_token_stream()),
 | |
|             AttrStyle::Inner(_) => Parser::parse2(Self::parse_inner, attr.to_token_stream()),
 | |
|         }
 | |
|     }
 | |
|     fn parse_starting_with_brackets(
 | |
|         pound_token: Token![#],
 | |
|         style: AttrStyle,
 | |
|         input: ParseStream,
 | |
|     ) -> syn::Result<Self> {
 | |
|         let bracket_content;
 | |
|         let bracket_token = bracketed!(bracket_content in input);
 | |
|         let kw = bracket_content.parse()?;
 | |
|         let paren_content;
 | |
|         let body;
 | |
|         let paren_token;
 | |
|         if bracket_content.is_empty() {
 | |
|             body = match syn::parse2(TokenStream::default()) {
 | |
|                 Ok(body) => body,
 | |
|                 Err(_) => {
 | |
|                     parenthesized!(paren_content in bracket_content);
 | |
|                     unreachable!();
 | |
|                 }
 | |
|             };
 | |
|             paren_token = None;
 | |
|         } else {
 | |
|             paren_token = Some(parenthesized!(paren_content in bracket_content));
 | |
|             body = paren_content.parse()?;
 | |
|         }
 | |
|         Ok(Self {
 | |
|             pound_token,
 | |
|             style,
 | |
|             bracket_token,
 | |
|             kw,
 | |
|             paren_token,
 | |
|             body,
 | |
|         })
 | |
|     }
 | |
|     fn parse_inner(input: ParseStream) -> syn::Result<Self> {
 | |
|         let pound_token = input.parse()?;
 | |
|         let style = AttrStyle::Inner(input.parse()?);
 | |
|         Self::parse_starting_with_brackets(pound_token, style, input)
 | |
|     }
 | |
|     fn parse_outer(input: ParseStream) -> syn::Result<Self> {
 | |
|         let pound_token = input.parse()?;
 | |
|         let style = AttrStyle::Outer;
 | |
|         Self::parse_starting_with_brackets(pound_token, style, input)
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[allow(dead_code)]
 | |
| pub(crate) trait PairsIterExt: Sized + Iterator {
 | |
|     fn map_pair<T1, T2, P1, P2, ValueFn: FnMut(T1) -> T2, PunctFn: FnMut(P1) -> P2>(
 | |
|         self,
 | |
|         mut value_fn: ValueFn,
 | |
|         mut punct_fn: PunctFn,
 | |
|     ) -> impl Iterator<Item = Pair<T2, P2>>
 | |
|     where
 | |
|         Self: Iterator<Item = Pair<T1, P1>>,
 | |
|     {
 | |
|         self.map(move |p| {
 | |
|             let (t, p) = p.into_tuple();
 | |
|             let t = value_fn(t);
 | |
|             let p = p.map(&mut punct_fn);
 | |
|             Pair::new(t, p)
 | |
|         })
 | |
|     }
 | |
|     fn filter_map_pair<T1, T2, P1, P2, ValueFn: FnMut(T1) -> Option<T2>, PunctFn: FnMut(P1) -> P2>(
 | |
|         self,
 | |
|         mut value_fn: ValueFn,
 | |
|         mut punct_fn: PunctFn,
 | |
|     ) -> impl Iterator<Item = Pair<T2, P2>>
 | |
|     where
 | |
|         Self: Iterator<Item = Pair<T1, P1>>,
 | |
|     {
 | |
|         self.filter_map(move |p| {
 | |
|             let (t, p) = p.into_tuple();
 | |
|             let t = value_fn(t)?;
 | |
|             let p = p.map(&mut punct_fn);
 | |
|             Some(Pair::new(t, p))
 | |
|         })
 | |
|     }
 | |
|     fn map_pair_value<T1, T2, P, F: FnMut(T1) -> T2>(
 | |
|         self,
 | |
|         f: F,
 | |
|     ) -> impl Iterator<Item = Pair<T2, P>>
 | |
|     where
 | |
|         Self: Iterator<Item = Pair<T1, P>>,
 | |
|     {
 | |
|         self.map_pair(f, |v| v)
 | |
|     }
 | |
|     fn filter_map_pair_value<T1, T2, P, F: FnMut(T1) -> Option<T2>>(
 | |
|         self,
 | |
|         f: F,
 | |
|     ) -> impl Iterator<Item = Pair<T2, P>>
 | |
|     where
 | |
|         Self: Iterator<Item = Pair<T1, P>>,
 | |
|     {
 | |
|         self.filter_map_pair(f, |v| v)
 | |
|     }
 | |
|     fn map_pair_value_mut<'a, T1: 'a, T2: 'a, P: Clone + 'a, F: FnMut(T1) -> T2 + 'a>(
 | |
|         self,
 | |
|         f: F,
 | |
|     ) -> impl Iterator<Item = Pair<T2, P>> + 'a
 | |
|     where
 | |
|         Self: Iterator<Item = Pair<T1, &'a mut P>> + 'a,
 | |
|     {
 | |
|         self.map_pair(f, |v| v.clone())
 | |
|     }
 | |
|     fn filter_map_pair_value_mut<
 | |
|         'a,
 | |
|         T1: 'a,
 | |
|         T2: 'a,
 | |
|         P: Clone + 'a,
 | |
|         F: FnMut(T1) -> Option<T2> + 'a,
 | |
|     >(
 | |
|         self,
 | |
|         f: F,
 | |
|     ) -> impl Iterator<Item = Pair<T2, P>> + 'a
 | |
|     where
 | |
|         Self: Iterator<Item = Pair<T1, &'a mut P>> + 'a,
 | |
|     {
 | |
|         self.filter_map_pair(f, |v| v.clone())
 | |
|     }
 | |
|     fn map_pair_value_ref<'a, T1: 'a, T2: 'a, P: Clone + 'a, F: FnMut(T1) -> T2 + 'a>(
 | |
|         self,
 | |
|         f: F,
 | |
|     ) -> impl Iterator<Item = Pair<T2, P>> + 'a
 | |
|     where
 | |
|         Self: Iterator<Item = Pair<T1, &'a P>> + 'a,
 | |
|     {
 | |
|         self.map_pair(f, |v| v.clone())
 | |
|     }
 | |
|     fn filter_map_pair_value_ref<
 | |
|         'a,
 | |
|         T1: 'a,
 | |
|         T2: 'a,
 | |
|         P: Clone + 'a,
 | |
|         F: FnMut(T1) -> Option<T2> + 'a,
 | |
|     >(
 | |
|         self,
 | |
|         f: F,
 | |
|     ) -> impl Iterator<Item = Pair<T2, P>> + 'a
 | |
|     where
 | |
|         Self: Iterator<Item = Pair<T1, &'a P>> + 'a,
 | |
|     {
 | |
|         self.filter_map_pair(f, |v| v.clone())
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl<T, P, Iter: Iterator<Item = Pair<T, P>>> PairsIterExt for Iter {}
 | |
| 
 | |
| pub(crate) struct Errors {
 | |
|     error: Option<Error>,
 | |
|     finished: bool,
 | |
| }
 | |
| 
 | |
| impl Drop for Errors {
 | |
|     fn drop(&mut self) {
 | |
|         if !std::thread::panicking() {
 | |
|             assert!(self.finished, "didn't run finish");
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl Errors {
 | |
|     pub(crate) fn new() -> Self {
 | |
|         Self {
 | |
|             error: None,
 | |
|             finished: false,
 | |
|         }
 | |
|     }
 | |
|     pub(crate) fn push(&mut self, e: Error) -> &mut Self {
 | |
|         match self.error {
 | |
|             Some(ref mut old) => old.combine(e),
 | |
|             None => self.error = Some(e),
 | |
|         }
 | |
|         self
 | |
|     }
 | |
|     pub(crate) fn push_result(&mut self, e: syn::Result<()>) -> &mut Self {
 | |
|         self.ok(e);
 | |
|         self
 | |
|     }
 | |
|     pub(crate) fn error(
 | |
|         &mut self,
 | |
|         tokens: impl ToTokens,
 | |
|         message: impl std::fmt::Display,
 | |
|     ) -> &mut Self {
 | |
|         self.push(Error::new_spanned(tokens, message));
 | |
|         self
 | |
|     }
 | |
|     pub(crate) fn ok<T>(&mut self, v: syn::Result<T>) -> Option<T> {
 | |
|         match v {
 | |
|             Ok(v) => Some(v),
 | |
|             Err(e) => {
 | |
|                 self.push(e);
 | |
|                 None
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     pub(crate) fn unwrap_or_else<T>(
 | |
|         &mut self,
 | |
|         v: syn::Result<T>,
 | |
|         fallback: impl FnOnce() -> T,
 | |
|     ) -> T {
 | |
|         match v {
 | |
|             Ok(v) => v,
 | |
|             Err(e) => {
 | |
|                 self.push(e);
 | |
|                 fallback()
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     pub(crate) fn unwrap_or<T>(&mut self, v: syn::Result<T>, fallback: T) -> T {
 | |
|         self.unwrap_or_else(v, || fallback)
 | |
|     }
 | |
|     pub(crate) fn unwrap_or_default<T: Default>(&mut self, v: syn::Result<T>) -> T {
 | |
|         self.unwrap_or_else(v, T::default)
 | |
|     }
 | |
|     pub(crate) fn finish(&mut self) -> syn::Result<()> {
 | |
|         self.finished = true;
 | |
|         match self.error.take() {
 | |
|             Some(e) => Err(e),
 | |
|             None => Ok(()),
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl Default for Errors {
 | |
|     fn default() -> Self {
 | |
|         Self::new()
 | |
|     }
 | |
| }
 | |
| 
 | |
| macro_rules! impl_extra_traits_for_options {
 | |
|     (
 | |
|         #[no_ident_fragment]
 | |
|         $enum_vis:vis enum $option_enum_name:ident {
 | |
|             $($Variant:ident($key:ident),)*
 | |
|         }
 | |
|     ) => {
 | |
|         impl Copy for $option_enum_name {}
 | |
|     };
 | |
|     (
 | |
|         $enum_vis:vis enum $option_enum_name:ident {
 | |
|             $($Variant:ident($key:ident),)*
 | |
|         }
 | |
|     ) => {
 | |
|         impl Copy for $option_enum_name {}
 | |
| 
 | |
|         impl PartialEq for $option_enum_name {
 | |
|             fn eq(&self, other: &Self) -> bool {
 | |
|                 self.cmp(other).is_eq()
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         impl Eq for $option_enum_name {}
 | |
| 
 | |
|         impl PartialOrd for $option_enum_name {
 | |
|             fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
 | |
|                 Some(self.cmp(other))
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         impl Ord for $option_enum_name {
 | |
|             fn cmp(&self, other: &Self) -> std::cmp::Ordering {
 | |
|                 self.variant().cmp(&other.variant())
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         impl quote::IdentFragment for $option_enum_name {
 | |
|             fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
 | |
|                 let _ = f;
 | |
|                 match *self {
 | |
|                     $(Self::$Variant(ref v) => quote::IdentFragment::fmt(&v.0, f),)*
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             fn span(&self) -> Option<proc_macro2::Span> {
 | |
|                 match *self {
 | |
|                     $(Self::$Variant(ref v) => quote::IdentFragment::span(&v.0),)*
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         impl $option_enum_name {
 | |
|             #[allow(dead_code)]
 | |
|             $enum_vis fn span(&self) -> proc_macro2::Span {
 | |
|                 quote::IdentFragment::span(self).unwrap()
 | |
|             }
 | |
|         }
 | |
|     };
 | |
|     (
 | |
|         $(#[no_ident_fragment])?
 | |
|         $enum_vis:vis enum $option_enum_name:ident {
 | |
|             $($Variant:ident($key:ident $(, $value:ty)?),)*
 | |
|         }
 | |
|     ) => {};
 | |
| }
 | |
| 
 | |
| pub(crate) use impl_extra_traits_for_options;
 | |
| 
 | |
| macro_rules! options {
 | |
|     (
 | |
|         #[options = $options_name:ident]
 | |
|         $($tt:tt)*
 | |
|     ) => {
 | |
|         crate::options! {
 | |
|             #[options = $options_name, punct = syn::Token![,], allow_duplicates = false]
 | |
|             $($tt)*
 | |
|         }
 | |
|     };
 | |
|     (
 | |
|         #[options = $options_name:ident, punct = $Punct:ty, allow_duplicates = true]
 | |
|         $(#[$($enum_meta:tt)*])*
 | |
|         $enum_vis:vis enum $option_enum_name:ident {
 | |
|             $($Variant:ident($key:ident $(, $value:ty)?),)*
 | |
|         }
 | |
|     ) => {
 | |
|         crate::options! {
 | |
|             #[options = $options_name, punct = $Punct, allow_duplicates = (true)]
 | |
|             $(#[$($enum_meta)*])*
 | |
|             $enum_vis enum $option_enum_name {
 | |
|                 $($Variant($key $(, $value)?),)*
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         impl Extend<$option_enum_name> for $options_name {
 | |
|             fn extend<T: IntoIterator<Item = $option_enum_name>>(&mut self, iter: T) {
 | |
|                 iter.into_iter().for_each(|v| match v {
 | |
|                     $($option_enum_name::$Variant(v) => {
 | |
|                         self.$key = Some(v);
 | |
|                     })*
 | |
|                 });
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         impl FromIterator<$option_enum_name> for $options_name {
 | |
|             fn from_iter<T: IntoIterator<Item = $option_enum_name>>(iter: T) -> Self {
 | |
|                 let mut retval = Self::default();
 | |
|                 retval.extend(iter);
 | |
|                 retval
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         impl Extend<$options_name> for $options_name {
 | |
|             fn extend<T: IntoIterator<Item = $options_name>>(&mut self, iter: T) {
 | |
|                 iter.into_iter().for_each(|v| {
 | |
|                     $(if let Some(v) = v.$key {
 | |
|                         self.$key = Some(v);
 | |
|                     })*
 | |
|                 });
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         impl FromIterator<$options_name> for $options_name {
 | |
|             fn from_iter<T: IntoIterator<Item = $options_name>>(iter: T) -> Self {
 | |
|                 let mut retval = Self::default();
 | |
|                 retval.extend(iter);
 | |
|                 retval
 | |
|             }
 | |
|         }
 | |
|     };
 | |
|     (
 | |
|         #[options = $options_name:ident, punct = $Punct:ty, allow_duplicates = $allow_duplicates:expr]
 | |
|         $(#[$($enum_meta:tt)*])*
 | |
|         $enum_vis:vis enum $option_enum_name:ident {
 | |
|             $($Variant:ident($key:ident $(, $value:ty)?),)*
 | |
|         }
 | |
|     ) => {
 | |
|         crate::options! {
 | |
|             $(#[$($enum_meta)*])*
 | |
|             $enum_vis enum $option_enum_name {
 | |
|                 $($Variant($key $(, $value)?),)*
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         #[derive(Clone, Debug, Default)]
 | |
|         #[allow(non_snake_case)]
 | |
|         $enum_vis struct $options_name {
 | |
|             $(
 | |
|                 $enum_vis $key: Option<(crate::kw::$key, $(syn::token::Paren, $value)?)>,
 | |
|             )*
 | |
|         }
 | |
| 
 | |
|         crate::fold::impl_fold! {
 | |
|             struct $options_name<> {
 | |
|                 $($key: Option<(crate::kw::$key, $(syn::token::Paren, $value)?)>,)*
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         const _: () = {
 | |
|             #[derive(Clone, Debug)]
 | |
|             $enum_vis struct Iter($enum_vis $options_name);
 | |
| 
 | |
|             impl IntoIterator for $options_name {
 | |
|                 type Item = $option_enum_name;
 | |
|                 type IntoIter = Iter;
 | |
| 
 | |
|                 fn into_iter(self) -> Self::IntoIter {
 | |
|                     Iter(self)
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             impl Iterator for Iter {
 | |
|                 type Item = $option_enum_name;
 | |
| 
 | |
|                 fn next(&mut self) -> Option<Self::Item> {
 | |
|                     $(
 | |
|                         if let Some(value) = self.0.$key.take() {
 | |
|                             return Some($option_enum_name::$Variant(value));
 | |
|                         }
 | |
|                     )*
 | |
|                     None
 | |
|                 }
 | |
| 
 | |
|                 #[allow(unused_mut, unused_variables)]
 | |
|                 fn fold<B, F: FnMut(B, Self::Item) -> B>(mut self, mut init: B, mut f: F) -> B {
 | |
|                     $(
 | |
|                         if let Some(value) = self.0.$key.take() {
 | |
|                             init = f(init, $option_enum_name::$Variant(value));
 | |
|                         }
 | |
|                     )*
 | |
|                     init
 | |
|                 }
 | |
|             }
 | |
|         };
 | |
| 
 | |
|         impl syn::parse::Parse for $options_name {
 | |
|             fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
 | |
|                 #![allow(unused_mut, unused_variables, unreachable_code)]
 | |
|                 let mut retval = Self::default();
 | |
|                 while !input.is_empty() {
 | |
|                     let old_input = input.fork();
 | |
|                     match input.parse::<$option_enum_name>()? {
 | |
|                         $($option_enum_name::$Variant(v) => {
 | |
|                             if retval.$key.replace(v).is_some() && !$allow_duplicates {
 | |
|                                 return Err(old_input.error(concat!("duplicate ", stringify!($key), " option")));
 | |
|                             }
 | |
|                         })*
 | |
|                     }
 | |
|                     if input.is_empty() {
 | |
|                         break;
 | |
|                     }
 | |
|                     input.parse::<$Punct>()?;
 | |
|                 }
 | |
|                 Ok(retval)
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         impl quote::ToTokens for $options_name {
 | |
|             #[allow(unused_mut, unused_variables, unused_assignments)]
 | |
|             fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
 | |
|                 let mut separator: Option<$Punct> = None;
 | |
|                 $(if let Some(v) = &self.$key {
 | |
|                     separator.to_tokens(tokens);
 | |
|                     separator = Some(Default::default());
 | |
|                     v.0.to_tokens(tokens);
 | |
|                     $(v.1.surround(
 | |
|                         tokens,
 | |
|                         |tokens| <$value as quote::ToTokens>::to_tokens(&v.2, tokens),
 | |
|                     );)?
 | |
|                 })*
 | |
|             }
 | |
|         }
 | |
|     };
 | |
|     (
 | |
|         $(#[$($enum_meta:tt)*])*
 | |
|         $enum_vis:vis enum $option_enum_name:ident {
 | |
|             $($Variant:ident($key:ident $(, $value:ty)?),)*
 | |
|         }
 | |
|     ) => {
 | |
|         #[derive(Clone, Debug)]
 | |
|         $enum_vis enum $option_enum_name {
 | |
|             $($Variant((crate::kw::$key, $(syn::token::Paren, $value)?)),)*
 | |
|         }
 | |
| 
 | |
|         crate::impl_extra_traits_for_options! {
 | |
|             $(#[$($enum_meta)*])*
 | |
|             $enum_vis enum $option_enum_name {
 | |
|                 $($Variant($key $(, $value)?),)*
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         crate::fold::impl_fold! {
 | |
|             enum $option_enum_name<> {
 | |
|                 $($Variant((crate::kw::$key, $(syn::token::Paren, $value)?)),)*
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         impl syn::parse::Parse for $option_enum_name {
 | |
|             fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
 | |
|                 let lookahead = input.lookahead1();
 | |
|                 $(
 | |
|                     if lookahead.peek(crate::kw::$key) {
 | |
|                         #[allow(unused_variables)]
 | |
|                         let paren_content: syn::parse::ParseBuffer;
 | |
|                         return Ok($option_enum_name::$Variant((
 | |
|                             input.parse()?,
 | |
|                             $(
 | |
|                                 syn::parenthesized!(paren_content in input),
 | |
|                                 paren_content.parse::<$value>()?,
 | |
|                             )?
 | |
|                         )));
 | |
|                     }
 | |
|                 )*
 | |
|                 Err(lookahead.error())
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         impl quote::ToTokens for $option_enum_name {
 | |
|             fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
 | |
|                 let _ = tokens;
 | |
|                 match *self {
 | |
|                     $($option_enum_name::$Variant(ref v) => {
 | |
|                         v.0.to_tokens(tokens);
 | |
|                         $(
 | |
|                             let value: &$value = &v.2;
 | |
|                             v.1.surround(tokens, |tokens| value.to_tokens(tokens));
 | |
|                         )?
 | |
|                     })*
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         impl $option_enum_name {
 | |
|             #[allow(dead_code)]
 | |
|             fn variant(&self) -> usize {
 | |
|                 #[repr(usize)]
 | |
|                 enum Variant {
 | |
|                     $($Variant,)*
 | |
|                     __Last, // so it doesn't complain about zero-variant enums
 | |
|                 }
 | |
|                 match *self {
 | |
|                     $(Self::$Variant(..) => Variant::$Variant as usize,)*
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     };
 | |
| }
 | |
| 
 | |
| use crate::hdl_type_alias::hdl_type_alias_impl;
 | |
| pub(crate) use options;
 | |
| 
 | |
| pub(crate) fn outline_generated(contents: TokenStream, prefix: &str) -> TokenStream {
 | |
|     let out_dir = env!("OUT_DIR");
 | |
|     let mut file = tempfile::Builder::new()
 | |
|         .prefix(prefix)
 | |
|         .rand_bytes(6)
 | |
|         .suffix(".tmp.rs")
 | |
|         .tempfile_in(out_dir)
 | |
|         .unwrap();
 | |
|     struct PrintOnPanic<'a>(&'a TokenStream);
 | |
|     impl Drop for PrintOnPanic<'_> {
 | |
|         fn drop(&mut self) {
 | |
|             if std::thread::panicking() {
 | |
|                 println!("{}", self.0);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     let _print_on_panic = PrintOnPanic(&contents);
 | |
|     let contents = prettyplease::unparse(&parse_quote! { #contents });
 | |
|     let hash = <sha2::Sha256 as sha2::Digest>::digest(&contents);
 | |
|     let hash = base16ct::HexDisplay(&hash[..5]);
 | |
|     file.write_all(contents.as_bytes()).unwrap();
 | |
|     let dest_file = std::path::Path::new(out_dir).join(format!("{prefix}{hash:x}.rs"));
 | |
|     // don't write if it already exists so cargo doesn't try to recompile constantly.
 | |
|     match file.persist_noclobber(&dest_file) {
 | |
|         Err(e) if e.error.kind() == ErrorKind::AlreadyExists => {}
 | |
|         e => {
 | |
|             e.unwrap();
 | |
|         }
 | |
|     }
 | |
|     eprintln!("generated {}", dest_file.display());
 | |
|     let dest_file = dest_file.to_str().unwrap();
 | |
| 
 | |
|     quote! {
 | |
|         include!(#dest_file);
 | |
|     }
 | |
| }
 | |
| 
 | |
| fn hdl_module_impl(item: ItemFn) -> syn::Result<TokenStream> {
 | |
|     let func = module::ModuleFn::parse_from_fn(item)?;
 | |
|     let options = func.config_options();
 | |
|     let mut contents = func.generate();
 | |
|     if options.outline_generated.is_some() {
 | |
|         contents = outline_generated(contents, "module-");
 | |
|     }
 | |
|     Ok(contents)
 | |
| }
 | |
| 
 | |
| #[derive(Clone, PartialEq, Eq, Hash, Debug)]
 | |
| pub(crate) enum CfgExpr {
 | |
|     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![,]>,
 | |
|     },
 | |
| }
 | |
| 
 | |
| impl Parse for CfgExpr {
 | |
|     fn parse(input: ParseStream) -> syn::Result<Self> {
 | |
|         match input.cursor().ident() {
 | |
|             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> {
 | |
|     fn parse_evaluated_cfgs_attr<R>(
 | |
|         input: ParseStream,
 | |
|         parse_inner: impl FnOnce(ParseStream) -> syn::Result<R>,
 | |
|     ) -> syn::Result<R> {
 | |
|         let _: Token![#] = input.parse()?;
 | |
|         let bracket_content;
 | |
|         bracketed!(bracket_content in input);
 | |
|         let _: kw::__evaluated_cfgs = bracket_content.parse()?;
 | |
|         let paren_content;
 | |
|         parenthesized!(paren_content in bracket_content);
 | |
|         parse_inner(&paren_content)
 | |
|     }
 | |
|     let (evaluated_cfgs, item): (_, TokenStream) = Parser::parse2(
 | |
|         |input: ParseStream| {
 | |
|             let peek = input.fork();
 | |
|             if parse_evaluated_cfgs_attr(&peek, |_| Ok(())).is_ok() {
 | |
|                 let evaluated_cfgs = parse_evaluated_cfgs_attr(input, Cfgs::<bool>::parse)?;
 | |
|                 Ok((Some(evaluated_cfgs), input.parse()?))
 | |
|             } else {
 | |
|                 Ok((None, input.parse()?))
 | |
|             }
 | |
|         },
 | |
|         item,
 | |
|     )?;
 | |
|     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 {
 | |
|             return Ok(quote! {
 | |
|                 ::fayalite::__cfg_expansion_helper! {
 | |
|                     []
 | |
|                     #cfgs
 | |
|                     {#[::fayalite::#kw(#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 {
 | |
|         Item::Enum(item) => hdl_enum::hdl_enum(item),
 | |
|         Item::Struct(item) => hdl_bundle::hdl_bundle(item),
 | |
|         Item::Fn(item) => hdl_module_impl(item),
 | |
|         Item::Type(item) => hdl_type_alias_impl(item),
 | |
|         _ => Err(syn::Error::new(
 | |
|             Span::call_site(),
 | |
|             "top-level #[hdl] can only be used on structs, enums, type aliases, or functions",
 | |
|         )),
 | |
|     }
 | |
| }
 | |
| 
 | |
| 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)
 | |
| }
 |