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, AttrStyle, Attribute, Error, Item, Token,
|
|
|
|
};
|
|
|
|
|
|
|
|
mod fold;
|
|
|
|
mod module;
|
|
|
|
mod value_derive_common;
|
|
|
|
mod value_derive_enum;
|
|
|
|
mod value_derive_struct;
|
|
|
|
|
|
|
|
mod kw {
|
|
|
|
pub(crate) use syn::token::{
|
|
|
|
Enum as enum_, Extern as extern_, Struct as struct_, Where as where_,
|
|
|
|
};
|
|
|
|
|
|
|
|
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);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
custom_keyword!(clock_domain);
|
|
|
|
custom_keyword!(connect_inexact);
|
|
|
|
custom_keyword!(fixed_type);
|
|
|
|
custom_keyword!(flip);
|
|
|
|
custom_keyword!(hdl);
|
|
|
|
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!(outline_generated);
|
|
|
|
custom_keyword!(output);
|
|
|
|
custom_keyword!(reg_builder);
|
|
|
|
custom_keyword!(reset);
|
|
|
|
custom_keyword!(reset_default);
|
|
|
|
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> {
|
|
|
|
pub(crate) pound_token: Pound,
|
|
|
|
pub(crate) style: AttrStyle,
|
|
|
|
pub(crate) bracket_token: syn::token::Bracket,
|
|
|
|
pub(crate) hdl: kw::hdl,
|
|
|
|
pub(crate) paren_token: Option<syn::token::Paren>,
|
|
|
|
pub(crate) body: T,
|
|
|
|
}
|
|
|
|
|
|
|
|
crate::fold::impl_fold! {
|
|
|
|
struct HdlAttr<T,> {
|
|
|
|
pound_token: Pound,
|
|
|
|
style: AttrStyle,
|
|
|
|
bracket_token: syn::token::Bracket,
|
|
|
|
hdl: kw::hdl,
|
|
|
|
paren_token: Option<syn::token::Paren>,
|
|
|
|
body: T,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(dead_code)]
|
|
|
|
impl<T> HdlAttr<T> {
|
|
|
|
pub(crate) fn split_body(self) -> (HdlAttr<()>, T) {
|
|
|
|
let Self {
|
|
|
|
pound_token,
|
|
|
|
style,
|
|
|
|
bracket_token,
|
|
|
|
hdl,
|
|
|
|
paren_token,
|
|
|
|
body,
|
|
|
|
} = self;
|
|
|
|
(
|
|
|
|
HdlAttr {
|
|
|
|
pound_token,
|
|
|
|
style,
|
|
|
|
bracket_token,
|
|
|
|
hdl,
|
|
|
|
paren_token,
|
|
|
|
body: (),
|
|
|
|
},
|
|
|
|
body,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
pub(crate) fn replace_body<T2>(self, body: T2) -> HdlAttr<T2> {
|
|
|
|
let Self {
|
|
|
|
pound_token,
|
|
|
|
style,
|
|
|
|
bracket_token,
|
|
|
|
hdl,
|
|
|
|
paren_token,
|
|
|
|
body: _,
|
|
|
|
} = self;
|
|
|
|
HdlAttr {
|
|
|
|
pound_token,
|
|
|
|
style,
|
|
|
|
bracket_token,
|
|
|
|
hdl,
|
|
|
|
paren_token,
|
|
|
|
body,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pub(crate) fn as_ref(&self) -> HdlAttr<&T> {
|
|
|
|
let Self {
|
|
|
|
pound_token,
|
|
|
|
style,
|
|
|
|
bracket_token,
|
|
|
|
hdl,
|
|
|
|
paren_token,
|
|
|
|
ref body,
|
|
|
|
} = *self;
|
|
|
|
HdlAttr {
|
|
|
|
pound_token,
|
|
|
|
style,
|
|
|
|
bracket_token,
|
|
|
|
hdl,
|
|
|
|
paren_token,
|
|
|
|
body,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pub(crate) fn try_map<R, E, F: FnOnce(T) -> Result<R, E>>(self, f: F) -> Result<HdlAttr<R>, E> {
|
|
|
|
let Self {
|
|
|
|
pound_token,
|
|
|
|
style,
|
|
|
|
bracket_token,
|
|
|
|
hdl,
|
|
|
|
paren_token,
|
|
|
|
body,
|
|
|
|
} = self;
|
|
|
|
Ok(HdlAttr {
|
|
|
|
pound_token,
|
|
|
|
style,
|
|
|
|
bracket_token,
|
|
|
|
hdl,
|
|
|
|
paren_token,
|
|
|
|
body: f(body)?,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
pub(crate) fn map<R, F: FnOnce(T) -> R>(self, f: F) -> HdlAttr<R> {
|
|
|
|
let Self {
|
|
|
|
pound_token,
|
|
|
|
style,
|
|
|
|
bracket_token,
|
|
|
|
hdl,
|
|
|
|
paren_token,
|
|
|
|
body,
|
|
|
|
} = self;
|
|
|
|
HdlAttr {
|
|
|
|
pound_token,
|
|
|
|
style,
|
|
|
|
bracket_token,
|
|
|
|
hdl,
|
|
|
|
paren_token,
|
|
|
|
body: f(body),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fn to_attr(&self) -> Attribute
|
|
|
|
where
|
|
|
|
T: ToTokens,
|
|
|
|
{
|
|
|
|
parse_quote! { #self }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: Default> Default for HdlAttr<T> {
|
|
|
|
fn default() -> Self {
|
|
|
|
T::default().into()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> From<T> for HdlAttr<T> {
|
|
|
|
fn from(body: T) -> Self {
|
|
|
|
HdlAttr {
|
|
|
|
pound_token: Default::default(),
|
|
|
|
style: AttrStyle::Outer,
|
|
|
|
bracket_token: Default::default(),
|
|
|
|
hdl: Default::default(),
|
|
|
|
paren_token: Default::default(),
|
|
|
|
body,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: ToTokens> ToTokens for HdlAttr<T> {
|
|
|
|
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.hdl.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.hdl.span)
|
|
|
|
.surround(tokens, |tokens| tokens.extend([body]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_hdl_attr(attr: &Attribute) -> bool {
|
|
|
|
attr.path().is_ident("hdl")
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: Parse> HdlAttr<T> {
|
|
|
|
fn parse_and_take_attr(attrs: &mut Vec<Attribute>) -> syn::Result<Option<Self>> {
|
|
|
|
let mut retval = None;
|
|
|
|
let mut errors = Errors::new();
|
|
|
|
attrs.retain(|attr| {
|
|
|
|
if is_hdl_attr(attr) {
|
|
|
|
if retval.is_some() {
|
|
|
|
errors.push(Error::new_spanned(attr, "more than one #[hdl] attribute"));
|
|
|
|
}
|
|
|
|
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>> {
|
|
|
|
let mut retval = None;
|
|
|
|
let mut errors = Errors::new();
|
|
|
|
for attr in attrs {
|
|
|
|
if is_hdl_attr(attr) {
|
|
|
|
if retval.is_some() {
|
|
|
|
errors.push(Error::new_spanned(attr, "more than one #[hdl] attribute"));
|
|
|
|
}
|
|
|
|
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 hdl = 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,
|
|
|
|
hdl,
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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 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]
|
|
|
|
$(#[$($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)]
|
|
|
|
$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)?)>,)*
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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() {
|
|
|
|
return Err(old_input.error(concat!("duplicate ", stringify!($key), " option")));
|
|
|
|
}
|
|
|
|
})*
|
|
|
|
}
|
|
|
|
if input.is_empty() {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
input.parse::<syn::Token![,]>()?;
|
|
|
|
}
|
|
|
|
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<syn::Token![,]> = None;
|
|
|
|
$(if let Some(v) = &self.$key {
|
|
|
|
separator.to_tokens(tokens);
|
|
|
|
separator = Some(Default::default());
|
|
|
|
v.0.to_tokens(tokens);
|
2024-07-11 06:34:41 +00:00
|
|
|
$(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));
|
|
|
|
)?
|
|
|
|
})*
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn module(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> {
|
|
|
|
let options = syn::parse2::<module::ConfigOptions>(attr)?;
|
|
|
|
let options = HdlAttr::from(options);
|
|
|
|
let func = syn::parse2::<module::ModuleFn>(quote! { #options #item })?;
|
|
|
|
let mut contents = func.generate();
|
|
|
|
if options.body.outline_generated.is_some() {
|
|
|
|
contents = outline_generated(contents, "module-");
|
|
|
|
}
|
|
|
|
Ok(contents)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn value_derive(item: TokenStream) -> syn::Result<TokenStream> {
|
|
|
|
let item = syn::parse2::<Item>(item)?;
|
|
|
|
match item {
|
|
|
|
Item::Enum(item) => value_derive_enum::value_derive_enum(item),
|
|
|
|
Item::Struct(item) => value_derive_struct::value_derive_struct(item),
|
|
|
|
_ => Err(syn::Error::new(
|
|
|
|
Span::call_site(),
|
|
|
|
"derive(Value) can only be used on structs or enums",
|
|
|
|
)),
|
|
|
|
}
|
|
|
|
}
|