fayalite/crates/fayalite-proc-macros-impl/src/lib.rs

924 lines
27 KiB
Rust
Raw Normal View History

2024-06-11 06:09:13 +00:00
// 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::{quote, ToTokens};
use std::io::{ErrorKind, Write};
use syn::{
bracketed, parenthesized,
parse::{Parse, ParseStream, Parser},
parse_quote,
punctuated::Pair,
spanned::Spanned,
AttrStyle, Attribute, Error, Item, ItemFn, Token,
2024-06-11 06:09:13 +00:00
};
mod fold;
mod hdl_bundle;
mod hdl_enum;
2024-10-31 03:47:10 +00:00
mod hdl_type_alias;
mod hdl_type_common;
2024-06-11 06:09:13 +00:00
mod module;
pub(crate) trait CustomToken:
Copy
+ Spanned
+ ToTokens
+ std::fmt::Debug
+ Eq
+ std::hash::Hash
+ Default
+ quote::IdentFragment
+ Parse
{
const IDENT_STR: &'static str;
}
2024-06-11 06:09:13 +00:00
mod kw {
pub(crate) use syn::token::Extern as extern_;
2024-06-11 06:09:13 +00:00
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);
}
2024-06-11 06:09:13 +00:00
};
}
custom_keyword!(clock_domain);
custom_keyword!(connect_inexact);
custom_keyword!(custom_bounds);
2024-06-11 06:09:13 +00:00
custom_keyword!(flip);
custom_keyword!(hdl);
custom_keyword!(hdl_module);
2024-06-11 06:09:13 +00:00
custom_keyword!(input);
custom_keyword!(incomplete_wire);
2024-06-11 06:09:13 +00:00
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);
2024-06-11 06:09:13 +00:00
custom_keyword!(outline_generated);
custom_keyword!(output);
custom_keyword!(reg_builder);
custom_keyword!(reset);
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> {
2024-06-11 06:09:13 +00:00
pub(crate) pound_token: Pound,
pub(crate) style: AttrStyle,
pub(crate) bracket_token: syn::token::Bracket,
pub(crate) kw: KW,
2024-06-11 06:09:13 +00:00
pub(crate) paren_token: Option<syn::token::Paren>,
pub(crate) body: T,
}
crate::fold::impl_fold! {
struct HdlAttr<T, KW,> {
2024-06-11 06:09:13 +00:00
pound_token: Pound,
style: AttrStyle,
bracket_token: syn::token::Bracket,
kw: KW,
2024-06-11 06:09:13 +00:00
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) {
2024-06-11 06:09:13 +00:00
let Self {
pound_token,
style,
bracket_token,
kw,
2024-06-11 06:09:13 +00:00
paren_token,
body,
} = self;
(
HdlAttr {
pound_token,
style,
bracket_token,
kw,
2024-06-11 06:09:13 +00:00
paren_token,
body: (),
},
body,
)
}
pub(crate) fn replace_body<T2>(self, body: T2) -> HdlAttr<T2, KW> {
2024-06-11 06:09:13 +00:00
let Self {
pound_token,
style,
bracket_token,
kw,
2024-06-11 06:09:13 +00:00
paren_token,
body: _,
} = self;
HdlAttr {
pound_token,
style,
bracket_token,
kw,
2024-06-11 06:09:13 +00:00
paren_token,
body,
}
}
pub(crate) fn as_ref(&self) -> HdlAttr<&T, KW>
where
KW: Clone,
{
2024-06-11 06:09:13 +00:00
let Self {
pound_token,
style,
bracket_token,
ref kw,
2024-06-11 06:09:13 +00:00
paren_token,
ref body,
} = *self;
HdlAttr {
pound_token,
style,
bracket_token,
kw: kw.clone(),
2024-06-11 06:09:13 +00:00
paren_token,
body,
}
}
pub(crate) fn try_map<R, E, F: FnOnce(T) -> Result<R, E>>(
self,
f: F,
) -> Result<HdlAttr<R, KW>, E> {
2024-06-11 06:09:13 +00:00
let Self {
pound_token,
style,
bracket_token,
kw,
2024-06-11 06:09:13 +00:00
paren_token,
body,
} = self;
Ok(HdlAttr {
pound_token,
style,
bracket_token,
kw,
2024-06-11 06:09:13 +00:00
paren_token,
body: f(body)?,
})
}
pub(crate) fn map<R, F: FnOnce(T) -> R>(self, f: F) -> HdlAttr<R, KW> {
2024-06-11 06:09:13 +00:00
let Self {
pound_token,
style,
bracket_token,
kw,
2024-06-11 06:09:13 +00:00
paren_token,
body,
} = self;
HdlAttr {
pound_token,
style,
bracket_token,
kw,
2024-06-11 06:09:13 +00:00
paren_token,
body: f(body),
}
}
fn to_attr(&self) -> Attribute
where
T: ToTokens,
KW: ToTokens,
2024-06-11 06:09:13 +00:00
{
parse_quote! { #self }
}
}
impl<T: Default, KW: Default> Default for HdlAttr<T, KW> {
2024-06-11 06:09:13 +00:00
fn default() -> Self {
T::default().into()
}
}
impl<T, KW: Default> From<T> for HdlAttr<T, KW> {
2024-06-11 06:09:13 +00:00
fn from(body: T) -> Self {
HdlAttr {
pound_token: Default::default(),
style: AttrStyle::Outer,
bracket_token: Default::default(),
kw: Default::default(),
2024-06-11 06:09:13 +00:00
paren_token: Default::default(),
body,
}
}
}
impl<T: ToTokens, KW: ToTokens + Spanned> ToTokens for HdlAttr<T, KW> {
2024-06-11 06:09:13 +00:00
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);
2024-06-11 06:09:13 +00:00
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())
2024-06-11 06:09:13 +00:00
.surround(tokens, |tokens| tokens.extend([body]));
}
}
}
});
}
}
fn is_hdl_attr<KW: CustomToken>(attr: &Attribute) -> bool {
attr.path().is_ident(KW::IDENT_STR)
2024-06-11 06:09:13 +00:00
}
impl<T: Parse, KW: Parse> HdlAttr<T, KW> {
fn parse_and_take_attr(attrs: &mut Vec<Attribute>) -> syn::Result<Option<Self>>
where
KW: ToTokens,
{
2024-06-11 06:09:13 +00:00
let mut retval = None;
let mut errors = Errors::new();
attrs.retain(|attr| {
if let Ok(kw) = syn::parse2::<KW>(attr.path().to_token_stream()) {
2024-06-11 06:09:13 +00:00
if retval.is_some() {
errors.push(Error::new_spanned(
attr,
format_args!("more than one #[{}] attribute", kw.to_token_stream()),
));
2024-06-11 06:09:13 +00:00
}
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,
{
2024-06-11 06:09:13 +00:00
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()) {
2024-06-11 06:09:13 +00:00
if retval.is_some() {
errors.push(Error::new_spanned(
attr,
format_args!("more than one #[{}] attribute", kw.to_token_stream()),
));
2024-06-11 06:09:13 +00:00
}
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()?;
2024-06-11 06:09:13 +00:00
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,
2024-06-11 06:09:13 +00:00
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 {}
2024-06-11 06:09:13 +00:00
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())
}
}
2024-06-11 06:09:13 +00:00
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]
2024-06-11 06:09:13 +00:00
$(#[$($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)]
2024-06-11 06:09:13 +00:00
$enum_vis struct $options_name {
$(
$enum_vis $key: Option<(crate::kw::$key, $(syn::token::Paren, $value)?)>,
)*
2024-06-11 06:09:13 +00:00
}
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
}
}
};
2024-06-11 06:09:13 +00:00
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 {
2024-06-11 06:09:13 +00:00
return Err(old_input.error(concat!("duplicate ", stringify!($key), " option")));
}
})*
}
if input.is_empty() {
break;
}
input.parse::<$Punct>()?;
2024-06-11 06:09:13 +00:00
}
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;
2024-06-11 06:09:13 +00:00
$(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),
);)?
2024-06-11 06:09:13 +00:00
})*
}
}
};
(
$(#[$($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,)*
}
}
}
2024-06-11 06:09:13 +00:00
};
}
2024-10-31 03:47:10 +00:00
use crate::hdl_type_alias::hdl_type_alias_impl;
2024-06-11 06:09:13 +00:00
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);
2024-06-11 06:09:13 +00:00
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();
2024-06-11 06:09:13 +00:00
let mut contents = func.generate();
if options.outline_generated.is_some() {
2024-06-11 06:09:13 +00:00
contents = outline_generated(contents, "module-");
}
Ok(contents)
}
pub fn hdl_module(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> {
let kw = kw::hdl_module::default();
hdl_module_impl(syn::parse2(quote! { #[#kw(#attr)] #item })?)
}
pub fn hdl_attr(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> {
let kw = kw::hdl::default();
2024-10-31 03:47:10 +00:00
let item = quote! { #[#kw(#attr)] #item };
let item = syn::parse2::<Item>(item)?;
2024-06-11 06:09:13 +00:00
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),
2024-10-31 03:47:10 +00:00
Item::Type(item) => hdl_type_alias_impl(item),
2024-06-11 06:09:13 +00:00
_ => Err(syn::Error::new(
Span::call_site(),
2024-10-31 03:47:10 +00:00
"top-level #[hdl] can only be used on structs, enums, type aliases, or functions",
2024-06-11 06:09:13 +00:00
)),
}
}