codegen/
lib.rs

1//! Procedural macros for the Substrate analog circuit generator framework.
2#![warn(missing_docs)]
3
4pub(crate) mod block;
5pub(crate) mod common;
6pub(crate) mod io;
7pub(crate) mod layout;
8pub(crate) mod schematic;
9pub(crate) mod simulation;
10
11use crate::io::bundle_kind;
12use crate::schematic::nested_data;
13use darling::FromDeriveInput;
14use macrotools::{handle_darling_error, handle_syn_error};
15use proc_macro::TokenStream;
16use proc_macro_crate::{FoundCrate, crate_name};
17use proc_macro2::{Span, TokenStream as TokenStream2};
18use quote::quote;
19use simulation::{SaveTuplesInput, save_tuples};
20use snippets::include_snippet;
21use syn::Ident;
22use syn::{DeriveInput, parse_macro_input};
23
24/// Derives `Io` for a struct.
25///
26/// # Examples
27///
28/// By default, deriving `Io` for a struct creates two new structs, one corresponding to the IO's `BundleKind`
29/// and the other to relevant views of the IO. Relevant schematic and layout
30/// traits are automatically implemented using these two additional structs.
31///
32#[doc = include_snippet!("substrate", "buffer_io_simple")]
33#[proc_macro_derive(Io, attributes(substrate))]
34pub fn derive_io(input: TokenStream) -> TokenStream {
35    let parsed = parse_macro_input!(input as DeriveInput);
36    let bundle_impl = handle_syn_error!(bundle_kind(&parsed, true));
37    quote!(
38        #bundle_impl
39    )
40    .into()
41}
42
43/// Derives `BundleKind` for a struct.
44///
45/// Creates a struct representing relevant views of the bundle kind (called a `Bundle`).
46/// Relevant schematic and layout traits are automatically implemented using this additional struct.
47#[proc_macro_derive(BundleKind, attributes(substrate))]
48pub fn derive_bundle_kind(input: TokenStream) -> TokenStream {
49    let parsed = parse_macro_input!(input as DeriveInput);
50    let bundle_impl = handle_syn_error!(bundle_kind(&parsed, false));
51    quote!(
52        #bundle_impl
53    )
54    .into()
55}
56
57/// Derives `substrate::schematic::NestedData` for a struct.
58#[proc_macro_derive(NestedData, attributes(substrate))]
59pub fn derive_nested_data(input: TokenStream) -> TokenStream {
60    let parsed = parse_macro_input!(input as DeriveInput);
61    let output = handle_syn_error!(nested_data(&parsed));
62    quote!(
63        #output
64    )
65    .into()
66}
67
68/// Derives `substrate::block::Block` for a struct or enum.
69///
70/// You must specify the block's IO by adding a `#[substrate(io = "IoType")]` attribute:
71/// ```
72/// use substrate::block::Block;
73///
74/// #[derive(Block, Copy, Clone, Eq, PartialEq, Hash, Debug)]
75/// #[substrate(io = "substrate::types::TestbenchIo")]
76/// pub struct MyBlock {
77///   // ...
78/// }
79/// ```
80///
81/// This derive macro only works if you want to use the default value of the IO.
82/// If the IO type does not implement [`Default`], or you want to use a non-default
83/// value, you must implement `Block` manually.
84#[proc_macro_derive(Block, attributes(substrate))]
85pub fn derive_block(input: TokenStream) -> TokenStream {
86    let receiver =
87        block::BlockInputReceiver::from_derive_input(&parse_macro_input!(input as DeriveInput));
88    let receiver = handle_darling_error!(receiver);
89    quote!(
90        #receiver
91    )
92    .into()
93}
94
95/// Implements saving for tuples of analyses for a given schematic object.
96#[proc_macro]
97pub fn impl_save_tuples(item: TokenStream) -> TokenStream {
98    let parsed = parse_macro_input!(item as SaveTuplesInput);
99    let output = handle_syn_error!(save_tuples(parsed));
100
101    output.into()
102}
103
104pub(crate) fn substrate_ident() -> TokenStream2 {
105    match crate_name("substrate").expect("substrate is present in `Cargo.toml`") {
106        FoundCrate::Itself => quote!(::substrate),
107        FoundCrate::Name(name) => {
108            let ident = Ident::new(&name, Span::call_site());
109            quote!(::#ident)
110        }
111    }
112}