fayalite_proc_macros_impl/
lib.rs

1// SPDX-License-Identifier: LGPL-3.0-or-later
2// See Notices.txt for copyright information
3#![cfg_attr(test, recursion_limit = "512")]
4use proc_macro2::{Span, TokenStream};
5use quote::{quote, ToTokens};
6use std::io::{ErrorKind, Write};
7use syn::{
8    bracketed, parenthesized,
9    parse::{Parse, ParseStream, Parser},
10    parse_quote,
11    punctuated::Pair,
12    spanned::Spanned,
13    AttrStyle, Attribute, Error, Item, ItemFn, Token,
14};
15
16mod fold;
17mod hdl_bundle;
18mod hdl_enum;
19mod hdl_type_common;
20mod module;
21
22pub(crate) trait CustomToken:
23    Copy
24    + Spanned
25    + ToTokens
26    + std::fmt::Debug
27    + Eq
28    + std::hash::Hash
29    + Default
30    + quote::IdentFragment
31    + Parse
32{
33    const IDENT_STR: &'static str;
34}
35
36mod kw {
37    pub(crate) use syn::token::Extern as extern_;
38
39    macro_rules! custom_keyword {
40        ($kw:ident) => {
41            syn::custom_keyword!($kw);
42
43            impl quote::IdentFragment for $kw {
44                fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
45                    f.write_str(stringify!($kw))
46                }
47
48                fn span(&self) -> Option<proc_macro2::Span> {
49                    Some(self.span)
50                }
51            }
52
53            crate::fold::no_op_fold!($kw);
54
55            impl crate::CustomToken for $kw {
56                const IDENT_STR: &'static str = stringify!($kw);
57            }
58        };
59    }
60
61    custom_keyword!(clock_domain);
62    custom_keyword!(connect_inexact);
63    custom_keyword!(custom_bounds);
64    custom_keyword!(flip);
65    custom_keyword!(hdl);
66    custom_keyword!(hdl_module);
67    custom_keyword!(input);
68    custom_keyword!(incomplete_wire);
69    custom_keyword!(instance);
70    custom_keyword!(m);
71    custom_keyword!(memory);
72    custom_keyword!(memory_array);
73    custom_keyword!(memory_with_init);
74    custom_keyword!(no_reset);
75    custom_keyword!(no_runtime_generics);
76    custom_keyword!(no_static);
77    custom_keyword!(outline_generated);
78    custom_keyword!(output);
79    custom_keyword!(reg_builder);
80    custom_keyword!(reset);
81    custom_keyword!(skip);
82    custom_keyword!(target);
83    custom_keyword!(wire);
84}
85
86type Pound = Token![#]; // work around https://github.com/rust-lang/rust/issues/50676
87
88#[derive(Clone, Debug)]
89pub(crate) struct HdlAttr<T, KW> {
90    pub(crate) pound_token: Pound,
91    pub(crate) style: AttrStyle,
92    pub(crate) bracket_token: syn::token::Bracket,
93    pub(crate) kw: KW,
94    pub(crate) paren_token: Option<syn::token::Paren>,
95    pub(crate) body: T,
96}
97
98crate::fold::impl_fold! {
99    struct HdlAttr<T, KW,> {
100        pound_token: Pound,
101        style: AttrStyle,
102        bracket_token: syn::token::Bracket,
103        kw: KW,
104        paren_token: Option<syn::token::Paren>,
105        body: T,
106    }
107}
108
109#[allow(dead_code)]
110impl<T, KW> HdlAttr<T, KW> {
111    pub(crate) fn split_body(self) -> (HdlAttr<(), KW>, T) {
112        let Self {
113            pound_token,
114            style,
115            bracket_token,
116            kw,
117            paren_token,
118            body,
119        } = self;
120        (
121            HdlAttr {
122                pound_token,
123                style,
124                bracket_token,
125                kw,
126                paren_token,
127                body: (),
128            },
129            body,
130        )
131    }
132    pub(crate) fn replace_body<T2>(self, body: T2) -> HdlAttr<T2, KW> {
133        let Self {
134            pound_token,
135            style,
136            bracket_token,
137            kw,
138            paren_token,
139            body: _,
140        } = self;
141        HdlAttr {
142            pound_token,
143            style,
144            bracket_token,
145            kw,
146            paren_token,
147            body,
148        }
149    }
150    pub(crate) fn as_ref(&self) -> HdlAttr<&T, KW>
151    where
152        KW: Clone,
153    {
154        let Self {
155            pound_token,
156            style,
157            bracket_token,
158            ref kw,
159            paren_token,
160            ref body,
161        } = *self;
162        HdlAttr {
163            pound_token,
164            style,
165            bracket_token,
166            kw: kw.clone(),
167            paren_token,
168            body,
169        }
170    }
171    pub(crate) fn try_map<R, E, F: FnOnce(T) -> Result<R, E>>(
172        self,
173        f: F,
174    ) -> Result<HdlAttr<R, KW>, E> {
175        let Self {
176            pound_token,
177            style,
178            bracket_token,
179            kw,
180            paren_token,
181            body,
182        } = self;
183        Ok(HdlAttr {
184            pound_token,
185            style,
186            bracket_token,
187            kw,
188            paren_token,
189            body: f(body)?,
190        })
191    }
192    pub(crate) fn map<R, F: FnOnce(T) -> R>(self, f: F) -> HdlAttr<R, KW> {
193        let Self {
194            pound_token,
195            style,
196            bracket_token,
197            kw,
198            paren_token,
199            body,
200        } = self;
201        HdlAttr {
202            pound_token,
203            style,
204            bracket_token,
205            kw,
206            paren_token,
207            body: f(body),
208        }
209    }
210    fn to_attr(&self) -> Attribute
211    where
212        T: ToTokens,
213        KW: ToTokens,
214    {
215        parse_quote! { #self }
216    }
217}
218
219impl<T: Default, KW: Default> Default for HdlAttr<T, KW> {
220    fn default() -> Self {
221        T::default().into()
222    }
223}
224
225impl<T, KW: Default> From<T> for HdlAttr<T, KW> {
226    fn from(body: T) -> Self {
227        HdlAttr {
228            pound_token: Default::default(),
229            style: AttrStyle::Outer,
230            bracket_token: Default::default(),
231            kw: Default::default(),
232            paren_token: Default::default(),
233            body,
234        }
235    }
236}
237
238impl<T: ToTokens, KW: ToTokens + Spanned> ToTokens for HdlAttr<T, KW> {
239    fn to_tokens(&self, tokens: &mut TokenStream) {
240        self.pound_token.to_tokens(tokens);
241        match self.style {
242            AttrStyle::Inner(style) => style.to_tokens(tokens),
243            AttrStyle::Outer => {}
244        };
245        self.bracket_token.surround(tokens, |tokens| {
246            self.kw.to_tokens(tokens);
247            match self.paren_token {
248                Some(paren_token) => {
249                    paren_token.surround(tokens, |tokens| self.body.to_tokens(tokens))
250                }
251                None => {
252                    let body = self.body.to_token_stream();
253                    if !body.is_empty() {
254                        syn::token::Paren(self.kw.span())
255                            .surround(tokens, |tokens| tokens.extend([body]));
256                    }
257                }
258            }
259        });
260    }
261}
262
263fn is_hdl_attr<KW: CustomToken>(attr: &Attribute) -> bool {
264    attr.path().is_ident(KW::IDENT_STR)
265}
266
267impl<T: Parse, KW: Parse> HdlAttr<T, KW> {
268    fn parse_and_take_attr(attrs: &mut Vec<Attribute>) -> syn::Result<Option<Self>>
269    where
270        KW: ToTokens,
271    {
272        let mut retval = None;
273        let mut errors = Errors::new();
274        attrs.retain(|attr| {
275            if let Ok(kw) = syn::parse2::<KW>(attr.path().to_token_stream()) {
276                if retval.is_some() {
277                    errors.push(Error::new_spanned(
278                        attr,
279                        format_args!("more than one #[{}] attribute", kw.to_token_stream()),
280                    ));
281                }
282                errors.unwrap_or_default(Self::parse_attr(attr).map(|v| retval = Some(v)));
283                false
284            } else {
285                true
286            }
287        });
288        errors.finish()?;
289        Ok(retval)
290    }
291    fn parse_and_leave_attr(attrs: &[Attribute]) -> syn::Result<Option<Self>>
292    where
293        KW: ToTokens,
294    {
295        let mut retval = None;
296        let mut errors = Errors::new();
297        for attr in attrs {
298            if let Ok(kw) = syn::parse2::<KW>(attr.path().to_token_stream()) {
299                if retval.is_some() {
300                    errors.push(Error::new_spanned(
301                        attr,
302                        format_args!("more than one #[{}] attribute", kw.to_token_stream()),
303                    ));
304                }
305                errors.unwrap_or_default(Self::parse_attr(attr).map(|v| retval = Some(v)));
306            }
307        }
308        errors.finish()?;
309        Ok(retval)
310    }
311    fn parse_attr(attr: &Attribute) -> syn::Result<Self> {
312        match attr.style {
313            AttrStyle::Outer => Parser::parse2(Self::parse_outer, attr.to_token_stream()),
314            AttrStyle::Inner(_) => Parser::parse2(Self::parse_inner, attr.to_token_stream()),
315        }
316    }
317    fn parse_starting_with_brackets(
318        pound_token: Token![#],
319        style: AttrStyle,
320        input: ParseStream,
321    ) -> syn::Result<Self> {
322        let bracket_content;
323        let bracket_token = bracketed!(bracket_content in input);
324        let kw = bracket_content.parse()?;
325        let paren_content;
326        let body;
327        let paren_token;
328        if bracket_content.is_empty() {
329            body = match syn::parse2(TokenStream::default()) {
330                Ok(body) => body,
331                Err(_) => {
332                    parenthesized!(paren_content in bracket_content);
333                    unreachable!();
334                }
335            };
336            paren_token = None;
337        } else {
338            paren_token = Some(parenthesized!(paren_content in bracket_content));
339            body = paren_content.parse()?;
340        }
341        Ok(Self {
342            pound_token,
343            style,
344            bracket_token,
345            kw,
346            paren_token,
347            body,
348        })
349    }
350    fn parse_inner(input: ParseStream) -> syn::Result<Self> {
351        let pound_token = input.parse()?;
352        let style = AttrStyle::Inner(input.parse()?);
353        Self::parse_starting_with_brackets(pound_token, style, input)
354    }
355    fn parse_outer(input: ParseStream) -> syn::Result<Self> {
356        let pound_token = input.parse()?;
357        let style = AttrStyle::Outer;
358        Self::parse_starting_with_brackets(pound_token, style, input)
359    }
360}
361
362#[allow(dead_code)]
363pub(crate) trait PairsIterExt: Sized + Iterator {
364    fn map_pair<T1, T2, P1, P2, ValueFn: FnMut(T1) -> T2, PunctFn: FnMut(P1) -> P2>(
365        self,
366        mut value_fn: ValueFn,
367        mut punct_fn: PunctFn,
368    ) -> impl Iterator<Item = Pair<T2, P2>>
369    where
370        Self: Iterator<Item = Pair<T1, P1>>,
371    {
372        self.map(move |p| {
373            let (t, p) = p.into_tuple();
374            let t = value_fn(t);
375            let p = p.map(&mut punct_fn);
376            Pair::new(t, p)
377        })
378    }
379    fn filter_map_pair<T1, T2, P1, P2, ValueFn: FnMut(T1) -> Option<T2>, PunctFn: FnMut(P1) -> P2>(
380        self,
381        mut value_fn: ValueFn,
382        mut punct_fn: PunctFn,
383    ) -> impl Iterator<Item = Pair<T2, P2>>
384    where
385        Self: Iterator<Item = Pair<T1, P1>>,
386    {
387        self.filter_map(move |p| {
388            let (t, p) = p.into_tuple();
389            let t = value_fn(t)?;
390            let p = p.map(&mut punct_fn);
391            Some(Pair::new(t, p))
392        })
393    }
394    fn map_pair_value<T1, T2, P, F: FnMut(T1) -> T2>(
395        self,
396        f: F,
397    ) -> impl Iterator<Item = Pair<T2, P>>
398    where
399        Self: Iterator<Item = Pair<T1, P>>,
400    {
401        self.map_pair(f, |v| v)
402    }
403    fn filter_map_pair_value<T1, T2, P, F: FnMut(T1) -> Option<T2>>(
404        self,
405        f: F,
406    ) -> impl Iterator<Item = Pair<T2, P>>
407    where
408        Self: Iterator<Item = Pair<T1, P>>,
409    {
410        self.filter_map_pair(f, |v| v)
411    }
412    fn map_pair_value_mut<'a, T1: 'a, T2: 'a, P: Clone + 'a, F: FnMut(T1) -> T2 + 'a>(
413        self,
414        f: F,
415    ) -> impl Iterator<Item = Pair<T2, P>> + 'a
416    where
417        Self: Iterator<Item = Pair<T1, &'a mut P>> + 'a,
418    {
419        self.map_pair(f, |v| v.clone())
420    }
421    fn filter_map_pair_value_mut<
422        'a,
423        T1: 'a,
424        T2: 'a,
425        P: Clone + 'a,
426        F: FnMut(T1) -> Option<T2> + 'a,
427    >(
428        self,
429        f: F,
430    ) -> impl Iterator<Item = Pair<T2, P>> + 'a
431    where
432        Self: Iterator<Item = Pair<T1, &'a mut P>> + 'a,
433    {
434        self.filter_map_pair(f, |v| v.clone())
435    }
436    fn map_pair_value_ref<'a, T1: 'a, T2: 'a, P: Clone + 'a, F: FnMut(T1) -> T2 + 'a>(
437        self,
438        f: F,
439    ) -> impl Iterator<Item = Pair<T2, P>> + 'a
440    where
441        Self: Iterator<Item = Pair<T1, &'a P>> + 'a,
442    {
443        self.map_pair(f, |v| v.clone())
444    }
445    fn filter_map_pair_value_ref<
446        'a,
447        T1: 'a,
448        T2: 'a,
449        P: Clone + 'a,
450        F: FnMut(T1) -> Option<T2> + 'a,
451    >(
452        self,
453        f: F,
454    ) -> impl Iterator<Item = Pair<T2, P>> + 'a
455    where
456        Self: Iterator<Item = Pair<T1, &'a P>> + 'a,
457    {
458        self.filter_map_pair(f, |v| v.clone())
459    }
460}
461
462impl<T, P, Iter: Iterator<Item = Pair<T, P>>> PairsIterExt for Iter {}
463
464pub(crate) struct Errors {
465    error: Option<Error>,
466    finished: bool,
467}
468
469impl Drop for Errors {
470    fn drop(&mut self) {
471        if !std::thread::panicking() {
472            assert!(self.finished, "didn't run finish");
473        }
474    }
475}
476
477impl Errors {
478    pub(crate) fn new() -> Self {
479        Self {
480            error: None,
481            finished: false,
482        }
483    }
484    pub(crate) fn push(&mut self, e: Error) -> &mut Self {
485        match self.error {
486            Some(ref mut old) => old.combine(e),
487            None => self.error = Some(e),
488        }
489        self
490    }
491    pub(crate) fn push_result(&mut self, e: syn::Result<()>) -> &mut Self {
492        self.ok(e);
493        self
494    }
495    pub(crate) fn error(
496        &mut self,
497        tokens: impl ToTokens,
498        message: impl std::fmt::Display,
499    ) -> &mut Self {
500        self.push(Error::new_spanned(tokens, message));
501        self
502    }
503    pub(crate) fn ok<T>(&mut self, v: syn::Result<T>) -> Option<T> {
504        match v {
505            Ok(v) => Some(v),
506            Err(e) => {
507                self.push(e);
508                None
509            }
510        }
511    }
512    pub(crate) fn unwrap_or_else<T>(
513        &mut self,
514        v: syn::Result<T>,
515        fallback: impl FnOnce() -> T,
516    ) -> T {
517        match v {
518            Ok(v) => v,
519            Err(e) => {
520                self.push(e);
521                fallback()
522            }
523        }
524    }
525    pub(crate) fn unwrap_or<T>(&mut self, v: syn::Result<T>, fallback: T) -> T {
526        self.unwrap_or_else(v, || fallback)
527    }
528    pub(crate) fn unwrap_or_default<T: Default>(&mut self, v: syn::Result<T>) -> T {
529        self.unwrap_or_else(v, T::default)
530    }
531    pub(crate) fn finish(&mut self) -> syn::Result<()> {
532        self.finished = true;
533        match self.error.take() {
534            Some(e) => Err(e),
535            None => Ok(()),
536        }
537    }
538}
539
540impl Default for Errors {
541    fn default() -> Self {
542        Self::new()
543    }
544}
545
546macro_rules! impl_extra_traits_for_options {
547    (
548        #[no_ident_fragment]
549        $enum_vis:vis enum $option_enum_name:ident {
550            $($Variant:ident($key:ident),)*
551        }
552    ) => {
553        impl Copy for $option_enum_name {}
554    };
555    (
556        $enum_vis:vis enum $option_enum_name:ident {
557            $($Variant:ident($key:ident),)*
558        }
559    ) => {
560        impl Copy for $option_enum_name {}
561
562        impl PartialEq for $option_enum_name {
563            fn eq(&self, other: &Self) -> bool {
564                self.cmp(other).is_eq()
565            }
566        }
567
568        impl Eq for $option_enum_name {}
569
570        impl PartialOrd for $option_enum_name {
571            fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
572                Some(self.cmp(other))
573            }
574        }
575
576        impl Ord for $option_enum_name {
577            fn cmp(&self, other: &Self) -> std::cmp::Ordering {
578                self.variant().cmp(&other.variant())
579            }
580        }
581
582        impl quote::IdentFragment for $option_enum_name {
583            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
584                let _ = f;
585                match *self {
586                    $(Self::$Variant(ref v) => quote::IdentFragment::fmt(&v.0, f),)*
587                }
588            }
589
590            fn span(&self) -> Option<proc_macro2::Span> {
591                match *self {
592                    $(Self::$Variant(ref v) => quote::IdentFragment::span(&v.0),)*
593                }
594            }
595        }
596
597        impl $option_enum_name {
598            #[allow(dead_code)]
599            $enum_vis fn span(&self) -> proc_macro2::Span {
600                quote::IdentFragment::span(self).unwrap()
601            }
602        }
603    };
604    (
605        $(#[no_ident_fragment])?
606        $enum_vis:vis enum $option_enum_name:ident {
607            $($Variant:ident($key:ident $(, $value:ty)?),)*
608        }
609    ) => {};
610}
611
612pub(crate) use impl_extra_traits_for_options;
613
614macro_rules! options {
615    (
616        #[options = $options_name:ident]
617        $($tt:tt)*
618    ) => {
619        crate::options! {
620            #[options = $options_name, punct = syn::Token![,], allow_duplicates = false]
621            $($tt)*
622        }
623    };
624    (
625        #[options = $options_name:ident, punct = $Punct:ty, allow_duplicates = true]
626        $(#[$($enum_meta:tt)*])*
627        $enum_vis:vis enum $option_enum_name:ident {
628            $($Variant:ident($key:ident $(, $value:ty)?),)*
629        }
630    ) => {
631        crate::options! {
632            #[options = $options_name, punct = $Punct, allow_duplicates = (true)]
633            $(#[$($enum_meta)*])*
634            $enum_vis enum $option_enum_name {
635                $($Variant($key $(, $value)?),)*
636            }
637        }
638
639        impl Extend<$option_enum_name> for $options_name {
640            fn extend<T: IntoIterator<Item = $option_enum_name>>(&mut self, iter: T) {
641                iter.into_iter().for_each(|v| match v {
642                    $($option_enum_name::$Variant(v) => {
643                        self.$key = Some(v);
644                    })*
645                });
646            }
647        }
648
649        impl FromIterator<$option_enum_name> for $options_name {
650            fn from_iter<T: IntoIterator<Item = $option_enum_name>>(iter: T) -> Self {
651                let mut retval = Self::default();
652                retval.extend(iter);
653                retval
654            }
655        }
656
657        impl Extend<$options_name> for $options_name {
658            fn extend<T: IntoIterator<Item = $options_name>>(&mut self, iter: T) {
659                iter.into_iter().for_each(|v| {
660                    $(if let Some(v) = v.$key {
661                        self.$key = Some(v);
662                    })*
663                });
664            }
665        }
666
667        impl FromIterator<$options_name> for $options_name {
668            fn from_iter<T: IntoIterator<Item = $options_name>>(iter: T) -> Self {
669                let mut retval = Self::default();
670                retval.extend(iter);
671                retval
672            }
673        }
674    };
675    (
676        #[options = $options_name:ident, punct = $Punct:ty, allow_duplicates = $allow_duplicates:expr]
677        $(#[$($enum_meta:tt)*])*
678        $enum_vis:vis enum $option_enum_name:ident {
679            $($Variant:ident($key:ident $(, $value:ty)?),)*
680        }
681    ) => {
682        crate::options! {
683            $(#[$($enum_meta)*])*
684            $enum_vis enum $option_enum_name {
685                $($Variant($key $(, $value)?),)*
686            }
687        }
688
689        #[derive(Clone, Debug, Default)]
690        #[allow(non_snake_case)]
691        $enum_vis struct $options_name {
692            $(
693                $enum_vis $key: Option<(crate::kw::$key, $(syn::token::Paren, $value)?)>,
694            )*
695        }
696
697        crate::fold::impl_fold! {
698            struct $options_name<> {
699                $($key: Option<(crate::kw::$key, $(syn::token::Paren, $value)?)>,)*
700            }
701        }
702
703        const _: () = {
704            #[derive(Clone, Debug)]
705            $enum_vis struct Iter($enum_vis $options_name);
706
707            impl IntoIterator for $options_name {
708                type Item = $option_enum_name;
709                type IntoIter = Iter;
710
711                fn into_iter(self) -> Self::IntoIter {
712                    Iter(self)
713                }
714            }
715
716            impl Iterator for Iter {
717                type Item = $option_enum_name;
718
719                fn next(&mut self) -> Option<Self::Item> {
720                    $(
721                        if let Some(value) = self.0.$key.take() {
722                            return Some($option_enum_name::$Variant(value));
723                        }
724                    )*
725                    None
726                }
727
728                #[allow(unused_mut, unused_variables)]
729                fn fold<B, F: FnMut(B, Self::Item) -> B>(mut self, mut init: B, mut f: F) -> B {
730                    $(
731                        if let Some(value) = self.0.$key.take() {
732                            init = f(init, $option_enum_name::$Variant(value));
733                        }
734                    )*
735                    init
736                }
737            }
738        };
739
740        impl syn::parse::Parse for $options_name {
741            fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
742                #![allow(unused_mut, unused_variables, unreachable_code)]
743                let mut retval = Self::default();
744                while !input.is_empty() {
745                    let old_input = input.fork();
746                    match input.parse::<$option_enum_name>()? {
747                        $($option_enum_name::$Variant(v) => {
748                            if retval.$key.replace(v).is_some() && !$allow_duplicates {
749                                return Err(old_input.error(concat!("duplicate ", stringify!($key), " option")));
750                            }
751                        })*
752                    }
753                    if input.is_empty() {
754                        break;
755                    }
756                    input.parse::<$Punct>()?;
757                }
758                Ok(retval)
759            }
760        }
761
762        impl quote::ToTokens for $options_name {
763            #[allow(unused_mut, unused_variables, unused_assignments)]
764            fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
765                let mut separator: Option<$Punct> = None;
766                $(if let Some(v) = &self.$key {
767                    separator.to_tokens(tokens);
768                    separator = Some(Default::default());
769                    v.0.to_tokens(tokens);
770                    $(v.1.surround(
771                        tokens,
772                        |tokens| <$value as quote::ToTokens>::to_tokens(&v.2, tokens),
773                    );)?
774                })*
775            }
776        }
777    };
778    (
779        $(#[$($enum_meta:tt)*])*
780        $enum_vis:vis enum $option_enum_name:ident {
781            $($Variant:ident($key:ident $(, $value:ty)?),)*
782        }
783    ) => {
784        #[derive(Clone, Debug)]
785        $enum_vis enum $option_enum_name {
786            $($Variant((crate::kw::$key, $(syn::token::Paren, $value)?)),)*
787        }
788
789        crate::impl_extra_traits_for_options! {
790            $(#[$($enum_meta)*])*
791            $enum_vis enum $option_enum_name {
792                $($Variant($key $(, $value)?),)*
793            }
794        }
795
796        crate::fold::impl_fold! {
797            enum $option_enum_name<> {
798                $($Variant((crate::kw::$key, $(syn::token::Paren, $value)?)),)*
799            }
800        }
801
802        impl syn::parse::Parse for $option_enum_name {
803            fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
804                let lookahead = input.lookahead1();
805                $(
806                    if lookahead.peek(crate::kw::$key) {
807                        #[allow(unused_variables)]
808                        let paren_content: syn::parse::ParseBuffer;
809                        return Ok($option_enum_name::$Variant((
810                            input.parse()?,
811                            $(
812                                syn::parenthesized!(paren_content in input),
813                                paren_content.parse::<$value>()?,
814                            )?
815                        )));
816                    }
817                )*
818                Err(lookahead.error())
819            }
820        }
821
822        impl quote::ToTokens for $option_enum_name {
823            fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
824                let _ = tokens;
825                match *self {
826                    $($option_enum_name::$Variant(ref v) => {
827                        v.0.to_tokens(tokens);
828                        $(
829                            let value: &$value = &v.2;
830                            v.1.surround(tokens, |tokens| value.to_tokens(tokens));
831                        )?
832                    })*
833                }
834            }
835        }
836
837        impl $option_enum_name {
838            #[allow(dead_code)]
839            fn variant(&self) -> usize {
840                #[repr(usize)]
841                enum Variant {
842                    $($Variant,)*
843                    __Last, // so it doesn't complain about zero-variant enums
844                }
845                match *self {
846                    $(Self::$Variant(..) => Variant::$Variant as usize,)*
847                }
848            }
849        }
850    };
851}
852
853pub(crate) use options;
854
855pub(crate) fn outline_generated(contents: TokenStream, prefix: &str) -> TokenStream {
856    let out_dir = env!("OUT_DIR");
857    let mut file = tempfile::Builder::new()
858        .prefix(prefix)
859        .rand_bytes(6)
860        .suffix(".tmp.rs")
861        .tempfile_in(out_dir)
862        .unwrap();
863    struct PrintOnPanic<'a>(&'a TokenStream);
864    impl Drop for PrintOnPanic<'_> {
865        fn drop(&mut self) {
866            if std::thread::panicking() {
867                println!("{}", self.0);
868            }
869        }
870    }
871    let _print_on_panic = PrintOnPanic(&contents);
872    let contents = prettyplease::unparse(&parse_quote! { #contents });
873    let hash = <sha2::Sha256 as sha2::Digest>::digest(&contents);
874    let hash = base16ct::HexDisplay(&hash[..5]);
875    file.write_all(contents.as_bytes()).unwrap();
876    let dest_file = std::path::Path::new(out_dir).join(format!("{prefix}{hash:x}.rs"));
877    // don't write if it already exists so cargo doesn't try to recompile constantly.
878    match file.persist_noclobber(&dest_file) {
879        Err(e) if e.error.kind() == ErrorKind::AlreadyExists => {}
880        e => {
881            e.unwrap();
882        }
883    }
884    eprintln!("generated {}", dest_file.display());
885    let dest_file = dest_file.to_str().unwrap();
886
887    quote! {
888        include!(#dest_file);
889    }
890}
891
892fn hdl_module_impl(item: ItemFn) -> syn::Result<TokenStream> {
893    let func = module::ModuleFn::parse_from_fn(item)?;
894    let options = func.config_options();
895    let mut contents = func.generate();
896    if options.outline_generated.is_some() {
897        contents = outline_generated(contents, "module-");
898    }
899    Ok(contents)
900}
901
902pub fn hdl_module(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> {
903    let kw = kw::hdl_module::default();
904    hdl_module_impl(syn::parse2(quote! { #[#kw(#attr)] #item })?)
905}
906
907pub fn hdl_attr(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> {
908    let kw = kw::hdl::default();
909    let item = syn::parse2::<Item>(quote! { #[#kw(#attr)] #item })?;
910    match item {
911        Item::Enum(item) => hdl_enum::hdl_enum(item),
912        Item::Struct(item) => hdl_bundle::hdl_bundle(item),
913        Item::Fn(item) => hdl_module_impl(item),
914        _ => Err(syn::Error::new(
915            Span::call_site(),
916            "top-level #[hdl] can only be used on structs, enums, or functions",
917        )),
918    }
919}