WIP: use HdlOption[the_type_var] or UInt[123 + n] for creating types
All checks were successful
/ test (push) Successful in 13s
All checks were successful
/ test (push) Successful in 13s
This commit is contained in:
parent
cd99dbc849
commit
32bd3b99fd
29 changed files with 9753 additions and 7948 deletions
843
crates/fayalite-proc-macros-impl/src/hdl_bundle.rs
Normal file
843
crates/fayalite-proc-macros-impl/src/hdl_bundle.rs
Normal file
|
@ -0,0 +1,843 @@
|
|||
use crate::{
|
||||
hdl_type_common::{
|
||||
common_derives, get_target, ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedField,
|
||||
ParsedFieldsNamed, ParsedGenerics, SplitForImpl, TypesParser, WrappedInConst,
|
||||
},
|
||||
kw, Errors, HdlAttr, PairsIterExt,
|
||||
};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote_spanned, ToTokens};
|
||||
use syn::{
|
||||
parse_quote, parse_quote_spanned,
|
||||
punctuated::{Pair, Punctuated},
|
||||
spanned::Spanned,
|
||||
token::Brace,
|
||||
AngleBracketedGenericArguments, Attribute, Field, FieldMutability, Fields, FieldsNamed,
|
||||
GenericParam, Generics, Ident, ItemStruct, Path, Token, Type, Visibility,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct ParsedBundle {
|
||||
pub(crate) attrs: Vec<Attribute>,
|
||||
pub(crate) options: HdlAttr<ItemOptions>,
|
||||
pub(crate) vis: Visibility,
|
||||
pub(crate) struct_token: Token![struct],
|
||||
pub(crate) ident: Ident,
|
||||
pub(crate) generics: MaybeParsed<ParsedGenerics, Generics>,
|
||||
pub(crate) fields: MaybeParsed<ParsedFieldsNamed, FieldsNamed>,
|
||||
pub(crate) field_flips: Vec<Option<HdlAttr<kw::flip>>>,
|
||||
pub(crate) mask_type_ident: Ident,
|
||||
pub(crate) mask_type_match_variant_ident: Ident,
|
||||
pub(crate) match_variant_ident: Ident,
|
||||
pub(crate) builder_ident: Ident,
|
||||
pub(crate) mask_type_builder_ident: Ident,
|
||||
}
|
||||
|
||||
impl ParsedBundle {
|
||||
fn parse_field(
|
||||
errors: &mut Errors,
|
||||
field: &mut Field,
|
||||
index: usize,
|
||||
) -> Option<HdlAttr<kw::flip>> {
|
||||
let Field {
|
||||
attrs,
|
||||
vis: _,
|
||||
mutability,
|
||||
ident,
|
||||
colon_token,
|
||||
ty,
|
||||
} = field;
|
||||
let ident = ident.get_or_insert_with(|| format_ident!("_{}", index, span = ty.span()));
|
||||
if !matches!(mutability, FieldMutability::None) {
|
||||
// FIXME: use mutability as the spanned tokens,
|
||||
// blocked on https://github.com/dtolnay/syn/issues/1717
|
||||
errors.error(&ident, "field mutability is not supported");
|
||||
*mutability = FieldMutability::None;
|
||||
}
|
||||
*mutability = FieldMutability::None;
|
||||
colon_token.get_or_insert(Token));
|
||||
let options = errors.unwrap_or_default(HdlAttr::parse_and_take_attr(attrs));
|
||||
options
|
||||
}
|
||||
fn parse(item: ItemStruct) -> syn::Result<Self> {
|
||||
let ItemStruct {
|
||||
mut attrs,
|
||||
vis,
|
||||
struct_token,
|
||||
ident,
|
||||
mut generics,
|
||||
fields,
|
||||
semi_token,
|
||||
} = item;
|
||||
let mut errors = Errors::new();
|
||||
let mut options = errors
|
||||
.unwrap_or_default(HdlAttr::<ItemOptions>::parse_and_take_attr(&mut attrs))
|
||||
.unwrap_or_default();
|
||||
errors.ok(options.body.validate());
|
||||
let ItemOptions {
|
||||
outline_generated: _,
|
||||
connect_inexact: _,
|
||||
target: _,
|
||||
custom_bounds,
|
||||
no_static: _,
|
||||
} = options.body;
|
||||
let mut fields = match fields {
|
||||
syn::Fields::Named(fields) => fields,
|
||||
syn::Fields::Unnamed(fields) => {
|
||||
errors.error(&fields, "#[hdl] struct must use curly braces: {}");
|
||||
FieldsNamed {
|
||||
brace_token: Brace(fields.paren_token.span),
|
||||
named: fields.unnamed,
|
||||
}
|
||||
}
|
||||
syn::Fields::Unit => {
|
||||
errors.error(&fields, "#[hdl] struct must use curly braces: {}");
|
||||
FieldsNamed {
|
||||
brace_token: Brace(semi_token.unwrap_or_default().span),
|
||||
named: Punctuated::default(),
|
||||
}
|
||||
}
|
||||
};
|
||||
let mut field_flips = Vec::with_capacity(fields.named.len());
|
||||
for (index, field) in fields.named.iter_mut().enumerate() {
|
||||
field_flips.push(Self::parse_field(&mut errors, field, index));
|
||||
}
|
||||
let generics = if custom_bounds.is_some() {
|
||||
MaybeParsed::Unrecognized(generics)
|
||||
} else if let Some(generics) = errors.ok(ParsedGenerics::parse(&mut generics)) {
|
||||
MaybeParsed::Parsed(generics)
|
||||
} else {
|
||||
MaybeParsed::Unrecognized(generics)
|
||||
};
|
||||
let fields = TypesParser::maybe_run(generics.as_ref(), fields, &mut errors);
|
||||
errors.finish()?;
|
||||
Ok(Self {
|
||||
attrs,
|
||||
options,
|
||||
vis,
|
||||
struct_token,
|
||||
generics,
|
||||
fields,
|
||||
field_flips,
|
||||
mask_type_ident: format_ident!("__{}__MaskType", ident),
|
||||
mask_type_match_variant_ident: format_ident!("__{}__MaskType__MatchVariant", ident),
|
||||
match_variant_ident: format_ident!("__{}__MatchVariant", ident),
|
||||
mask_type_builder_ident: format_ident!("__{}__MaskType__Builder", ident),
|
||||
builder_ident: format_ident!("__{}__Builder", ident),
|
||||
ident,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct Builder {
|
||||
vis: Visibility,
|
||||
struct_token: Token![struct],
|
||||
ident: Ident,
|
||||
target: Path,
|
||||
generics: Generics,
|
||||
fields: FieldsNamed,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
enum BuilderFieldState {
|
||||
Unfilled,
|
||||
Generic,
|
||||
Filled,
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
fn phantom_field_name(&self) -> Ident {
|
||||
format_ident!("__phantom", span = self.ident.span())
|
||||
}
|
||||
fn phantom_field(&self) -> Field {
|
||||
let target = &self.target;
|
||||
let type_generics = self.generics.split_for_impl().1;
|
||||
Field {
|
||||
attrs: vec![],
|
||||
vis: Visibility::Inherited,
|
||||
mutability: FieldMutability::None,
|
||||
ident: Some(self.phantom_field_name()),
|
||||
colon_token: Some(Token)),
|
||||
ty: parse_quote_spanned! {self.ident.span()=>
|
||||
::fayalite::__std::marker::PhantomData<#target #type_generics>
|
||||
},
|
||||
}
|
||||
}
|
||||
fn builder_struct_generics(
|
||||
&self,
|
||||
mut get_field_state: impl FnMut(usize) -> BuilderFieldState,
|
||||
) -> Generics {
|
||||
let mut retval = self.generics.clone();
|
||||
for param in retval.params.iter_mut() {
|
||||
match param {
|
||||
GenericParam::Lifetime(_) => {}
|
||||
GenericParam::Type(param) => param.default = None,
|
||||
GenericParam::Const(param) => param.default = None,
|
||||
}
|
||||
}
|
||||
for (field_index, field) in self.fields.named.iter().enumerate() {
|
||||
match get_field_state(field_index) {
|
||||
BuilderFieldState::Unfilled | BuilderFieldState::Filled => continue,
|
||||
BuilderFieldState::Generic => {}
|
||||
}
|
||||
if !retval.params.empty_or_trailing() {
|
||||
retval.params.push_punct(Token));
|
||||
}
|
||||
retval.params.push_value(GenericParam::Type(
|
||||
type_var_for_field_name(field.ident.as_ref().unwrap()).into(),
|
||||
));
|
||||
}
|
||||
retval
|
||||
}
|
||||
fn builder_struct_ty(
|
||||
&self,
|
||||
mut get_field_state: impl FnMut(usize) -> BuilderFieldState,
|
||||
) -> Type {
|
||||
let mut ty_arguments: AngleBracketedGenericArguments = if self.generics.params.is_empty() {
|
||||
parse_quote_spanned! {self.ident.span()=>
|
||||
<>
|
||||
}
|
||||
} else {
|
||||
let builder_type_generics = self.generics.split_for_impl().1;
|
||||
parse_quote! { #builder_type_generics }
|
||||
};
|
||||
for (field_index, Field { ident, ty, .. }) in self.fields.named.iter().enumerate() {
|
||||
let ident = ident.as_ref().unwrap();
|
||||
if !ty_arguments.args.empty_or_trailing() {
|
||||
ty_arguments.args.push_punct(Token));
|
||||
}
|
||||
ty_arguments
|
||||
.args
|
||||
.push_value(match get_field_state(field_index) {
|
||||
BuilderFieldState::Unfilled => parse_quote_spanned! {self.ident.span()=>
|
||||
::fayalite::bundle::Unfilled<#ty>
|
||||
},
|
||||
BuilderFieldState::Generic => {
|
||||
let type_var = type_var_for_field_name(ident);
|
||||
parse_quote_spanned! {self.ident.span()=>
|
||||
#type_var
|
||||
}
|
||||
}
|
||||
BuilderFieldState::Filled => parse_quote_spanned! {self.ident.span()=>
|
||||
::fayalite::expr::Expr<#ty>
|
||||
},
|
||||
});
|
||||
}
|
||||
let ident = &self.ident;
|
||||
parse_quote_spanned! {ident.span()=>
|
||||
#ident #ty_arguments
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn type_var_for_field_name(ident: &Ident) -> Ident {
|
||||
format_ident!("__T_{}", ident)
|
||||
}
|
||||
|
||||
fn field_fn_for_field_name(ident: &Ident) -> Ident {
|
||||
format_ident!("field_{}", ident)
|
||||
}
|
||||
|
||||
impl ToTokens for Builder {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let Self {
|
||||
vis,
|
||||
struct_token,
|
||||
ident,
|
||||
target,
|
||||
generics: _,
|
||||
fields,
|
||||
} = self;
|
||||
let phantom_field_name = self.phantom_field_name();
|
||||
let builder_struct = ItemStruct {
|
||||
attrs: vec![parse_quote_spanned! {ident.span()=>
|
||||
#[allow(non_camel_case_types, dead_code)]
|
||||
}],
|
||||
vis: vis.clone(),
|
||||
struct_token: *struct_token,
|
||||
ident: ident.clone(),
|
||||
generics: self.builder_struct_generics(|_| BuilderFieldState::Generic),
|
||||
fields: Fields::Named(FieldsNamed {
|
||||
brace_token: fields.brace_token,
|
||||
named: Punctuated::from_iter(
|
||||
[Pair::Punctuated(
|
||||
self.phantom_field(),
|
||||
Token),
|
||||
)]
|
||||
.into_iter()
|
||||
.chain(fields.named.pairs().map_pair_value_ref(|field| {
|
||||
let ident = field.ident.as_ref().unwrap();
|
||||
let type_var = type_var_for_field_name(ident);
|
||||
Field {
|
||||
vis: Visibility::Inherited,
|
||||
ty: parse_quote_spanned! {ident.span()=>
|
||||
#type_var
|
||||
},
|
||||
..field.clone()
|
||||
}
|
||||
})),
|
||||
),
|
||||
}),
|
||||
semi_token: None,
|
||||
};
|
||||
builder_struct.to_tokens(tokens);
|
||||
let field_idents = Vec::from_iter(
|
||||
self.fields
|
||||
.named
|
||||
.iter()
|
||||
.map(|field| field.ident.as_ref().unwrap()),
|
||||
);
|
||||
for (
|
||||
field_index,
|
||||
Field {
|
||||
vis,
|
||||
ident: field_ident,
|
||||
ty,
|
||||
..
|
||||
},
|
||||
) in self.fields.named.iter().enumerate()
|
||||
{
|
||||
let field_ident = field_ident.as_ref().unwrap();
|
||||
let fn_ident = field_fn_for_field_name(field_ident);
|
||||
let fn_generics = self.builder_struct_generics(|i| {
|
||||
if i == field_index {
|
||||
BuilderFieldState::Unfilled
|
||||
} else {
|
||||
BuilderFieldState::Generic
|
||||
}
|
||||
});
|
||||
let (impl_generics, _, where_clause) = fn_generics.split_for_impl();
|
||||
let unfilled_ty = self.builder_struct_ty(|i| {
|
||||
if i == field_index {
|
||||
BuilderFieldState::Unfilled
|
||||
} else {
|
||||
BuilderFieldState::Generic
|
||||
}
|
||||
});
|
||||
let filled_ty = self.builder_struct_ty(|i| {
|
||||
if i == field_index {
|
||||
BuilderFieldState::Filled
|
||||
} else {
|
||||
BuilderFieldState::Generic
|
||||
}
|
||||
});
|
||||
let pat_fields =
|
||||
Vec::from_iter(self.fields.named.iter().enumerate().map(|(i, field)| {
|
||||
let field_ident = field.ident.as_ref().unwrap();
|
||||
if field_index == i {
|
||||
quote_spanned! {self.ident.span()=>
|
||||
#field_ident: _,
|
||||
}
|
||||
} else {
|
||||
quote_spanned! {self.ident.span()=>
|
||||
#field_ident,
|
||||
}
|
||||
}
|
||||
}));
|
||||
quote_spanned! {self.ident.span()=>
|
||||
#[automatically_derived]
|
||||
#[allow(non_camel_case_types, dead_code)]
|
||||
impl #impl_generics #unfilled_ty
|
||||
#where_clause
|
||||
{
|
||||
#vis fn #fn_ident(
|
||||
self,
|
||||
#field_ident: ::fayalite::expr::Expr<#ty>,
|
||||
) -> #filled_ty {
|
||||
let Self {
|
||||
#phantom_field_name: _,
|
||||
#(#pat_fields)*
|
||||
} = self;
|
||||
#ident {
|
||||
#phantom_field_name: ::fayalite::__std::marker::PhantomData,
|
||||
#(#field_idents,)*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
}
|
||||
let unfilled_generics = self.builder_struct_generics(|_| BuilderFieldState::Unfilled);
|
||||
let unfilled_ty = self.builder_struct_ty(|_| BuilderFieldState::Unfilled);
|
||||
let (unfilled_impl_generics, _, unfilled_where_clause) = unfilled_generics.split_for_impl();
|
||||
quote_spanned! {self.ident.span()=>
|
||||
#[automatically_derived]
|
||||
#[allow(non_camel_case_types, dead_code)]
|
||||
impl #unfilled_impl_generics ::fayalite::__std::default::Default for #unfilled_ty
|
||||
#unfilled_where_clause
|
||||
{
|
||||
fn default() -> Self {
|
||||
#ident {
|
||||
#phantom_field_name: ::fayalite::__std::marker::PhantomData,
|
||||
#(#field_idents: ::fayalite::__std::default::Default::default(),)*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
let filled_generics = self.builder_struct_generics(|_| BuilderFieldState::Filled);
|
||||
let filled_ty = self.builder_struct_ty(|_| BuilderFieldState::Filled);
|
||||
let (filled_impl_generics, _, filled_where_clause) = filled_generics.split_for_impl();
|
||||
let type_generics = self.generics.split_for_impl().1;
|
||||
quote_spanned! {self.ident.span()=>
|
||||
#[automatically_derived]
|
||||
#[allow(non_camel_case_types, dead_code)]
|
||||
impl #filled_impl_generics ::fayalite::expr::ToExpr for #filled_ty
|
||||
#filled_where_clause
|
||||
{
|
||||
type Type = #target #type_generics;
|
||||
fn to_expr(
|
||||
&self,
|
||||
) -> ::fayalite::expr::Expr<<Self as ::fayalite::expr::ToExpr>::Type> {
|
||||
let __ty = #target {
|
||||
#(#field_idents: ::fayalite::expr::Expr::ty(self.#field_idents),)*
|
||||
};
|
||||
let __field_values = [
|
||||
#(::fayalite::expr::Expr::canonical(self.#field_idents),)*
|
||||
];
|
||||
::fayalite::expr::ToExpr::to_expr(
|
||||
&::fayalite::expr::ops::BundleLiteral::new(__ty, &__field_values),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for ParsedBundle {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let Self {
|
||||
attrs,
|
||||
options,
|
||||
vis,
|
||||
struct_token,
|
||||
ident,
|
||||
generics,
|
||||
fields,
|
||||
field_flips,
|
||||
mask_type_ident,
|
||||
mask_type_match_variant_ident,
|
||||
match_variant_ident,
|
||||
builder_ident,
|
||||
mask_type_builder_ident,
|
||||
} = self;
|
||||
let ItemOptions {
|
||||
outline_generated: _,
|
||||
connect_inexact,
|
||||
target,
|
||||
custom_bounds: _,
|
||||
no_static,
|
||||
} = &options.body;
|
||||
let target = get_target(target, ident);
|
||||
let mut item_attrs = attrs.clone();
|
||||
item_attrs.push(common_derives(ident.span()));
|
||||
ItemStruct {
|
||||
attrs: item_attrs,
|
||||
vis: vis.clone(),
|
||||
struct_token: *struct_token,
|
||||
ident: ident.clone(),
|
||||
generics: generics.into(),
|
||||
fields: Fields::Named(fields.clone().into()),
|
||||
semi_token: None,
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
|
||||
if let (MaybeParsed::Parsed(generics), MaybeParsed::Parsed(fields)) = (generics, fields) {
|
||||
generics.make_runtime_generics(tokens, vis, ident, &target, |context| {
|
||||
let fields: Vec<_> = fields
|
||||
.named
|
||||
.iter()
|
||||
.map(|ParsedField { ident, ty, .. }| {
|
||||
let ident = ident.as_ref().unwrap();
|
||||
let expr = ty.make_hdl_type_expr(context);
|
||||
quote_spanned! {ident.span()=>
|
||||
#ident: #expr,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
parse_quote_spanned! {ident.span()=>
|
||||
#target {
|
||||
#(#fields)*
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
let mut wrapped_in_const = WrappedInConst::new(tokens, ident.span());
|
||||
let tokens = wrapped_in_const.inner();
|
||||
let builder = Builder {
|
||||
vis: vis.clone(),
|
||||
struct_token: *struct_token,
|
||||
ident: builder_ident.clone(),
|
||||
target: target.clone(),
|
||||
generics: generics.into(),
|
||||
fields: fields.clone().into(),
|
||||
};
|
||||
builder.to_tokens(tokens);
|
||||
let unfilled_builder_ty = builder.builder_struct_ty(|_| BuilderFieldState::Unfilled);
|
||||
let filled_builder_ty = builder.builder_struct_ty(|_| BuilderFieldState::Filled);
|
||||
let mut mask_type_fields = FieldsNamed::from(fields.clone());
|
||||
for Field { ident, ty, .. } in &mut mask_type_fields.named {
|
||||
let ident = ident.as_ref().unwrap();
|
||||
*ty = parse_quote_spanned! {ident.span()=>
|
||||
<#ty as ::fayalite::ty::Type>::MaskType
|
||||
};
|
||||
}
|
||||
let mask_type_builder = Builder {
|
||||
vis: vis.clone(),
|
||||
struct_token: *struct_token,
|
||||
ident: mask_type_builder_ident.clone(),
|
||||
target: mask_type_ident.clone().into(),
|
||||
generics: generics.into(),
|
||||
fields: mask_type_fields.clone(),
|
||||
};
|
||||
mask_type_builder.to_tokens(tokens);
|
||||
let unfilled_mask_type_builder_ty =
|
||||
mask_type_builder.builder_struct_ty(|_| BuilderFieldState::Unfilled);
|
||||
let filled_mask_type_builder_ty =
|
||||
mask_type_builder.builder_struct_ty(|_| BuilderFieldState::Filled);
|
||||
ItemStruct {
|
||||
attrs: vec![
|
||||
common_derives(ident.span()),
|
||||
parse_quote_spanned! {ident.span()=>
|
||||
#[allow(non_camel_case_types, dead_code)]
|
||||
},
|
||||
],
|
||||
vis: vis.clone(),
|
||||
struct_token: *struct_token,
|
||||
ident: mask_type_ident.clone(),
|
||||
generics: generics.into(),
|
||||
fields: Fields::Named(mask_type_fields.clone()),
|
||||
semi_token: None,
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
let mut mask_type_match_variant_fields = mask_type_fields;
|
||||
for Field { ident, ty, .. } in &mut mask_type_match_variant_fields.named {
|
||||
let ident = ident.as_ref().unwrap();
|
||||
*ty = parse_quote_spanned! {ident.span()=>
|
||||
::fayalite::expr::Expr<#ty>
|
||||
};
|
||||
}
|
||||
ItemStruct {
|
||||
attrs: vec![
|
||||
common_derives(ident.span()),
|
||||
parse_quote_spanned! {ident.span()=>
|
||||
#[allow(non_camel_case_types, dead_code)]
|
||||
},
|
||||
],
|
||||
vis: vis.clone(),
|
||||
struct_token: *struct_token,
|
||||
ident: mask_type_match_variant_ident.clone(),
|
||||
generics: generics.into(),
|
||||
fields: Fields::Named(mask_type_match_variant_fields),
|
||||
semi_token: None,
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
let mut match_variant_fields = FieldsNamed::from(fields.clone());
|
||||
for Field { ident, ty, .. } in &mut match_variant_fields.named {
|
||||
let ident = ident.as_ref().unwrap();
|
||||
*ty = parse_quote_spanned! {ident.span()=>
|
||||
::fayalite::expr::Expr<#ty>
|
||||
};
|
||||
}
|
||||
ItemStruct {
|
||||
attrs: vec![
|
||||
common_derives(ident.span()),
|
||||
parse_quote_spanned! {ident.span()=>
|
||||
#[allow(non_camel_case_types, dead_code)]
|
||||
},
|
||||
],
|
||||
vis: vis.clone(),
|
||||
struct_token: *struct_token,
|
||||
ident: match_variant_ident.clone(),
|
||||
generics: generics.into(),
|
||||
fields: Fields::Named(match_variant_fields),
|
||||
semi_token: None,
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
let match_variant_body_fields = Vec::from_iter(fields.named().into_iter().map(|field| {
|
||||
let ident: &Ident = field.ident().as_ref().unwrap();
|
||||
let ident_str = ident.to_string();
|
||||
quote_spanned! {ident.span()=>
|
||||
#ident: ::fayalite::expr::Expr::field(__this, #ident_str),
|
||||
}
|
||||
}));
|
||||
let mask_type_body_fields = Vec::from_iter(fields.named().into_iter().map(|field| {
|
||||
let ident: &Ident = field.ident().as_ref().unwrap();
|
||||
quote_spanned! {ident.span()=>
|
||||
#ident: ::fayalite::ty::Type::mask_type(&self.#ident),
|
||||
}
|
||||
}));
|
||||
let from_canonical_body_fields =
|
||||
Vec::from_iter(fields.named().into_iter().enumerate().zip(field_flips).map(
|
||||
|((index, field), flip)| {
|
||||
let ident: &Ident = field.ident().as_ref().unwrap();
|
||||
let ident_str = ident.to_string();
|
||||
let flipped = flip.is_some();
|
||||
quote_spanned! {ident.span()=>
|
||||
#ident: {
|
||||
let ::fayalite::bundle::BundleField {
|
||||
name: __name,
|
||||
flipped: __flipped,
|
||||
ty: __ty,
|
||||
} = __fields[#index];
|
||||
::fayalite::__std::assert_eq!(&*__name, #ident_str);
|
||||
::fayalite::__std::assert_eq!(__flipped, #flipped);
|
||||
::fayalite::ty::Type::from_canonical(__ty)
|
||||
},
|
||||
}
|
||||
},
|
||||
));
|
||||
let fields_body_fields = Vec::from_iter(fields.named().into_iter().zip(field_flips).map(
|
||||
|(field, flip)| {
|
||||
let ident: &Ident = field.ident().as_ref().unwrap();
|
||||
let ident_str = ident.to_string();
|
||||
let flipped = flip.is_some();
|
||||
quote_spanned! {ident.span()=>
|
||||
::fayalite::bundle::BundleField {
|
||||
name: ::fayalite::intern::Intern::intern(#ident_str),
|
||||
flipped: #flipped,
|
||||
ty: ::fayalite::ty::Type::canonical(&self.#ident),
|
||||
},
|
||||
}
|
||||
},
|
||||
));
|
||||
let fields_len = fields.named().into_iter().len();
|
||||
quote_spanned! {ident.span()=>
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::fayalite::ty::Type for #mask_type_ident #type_generics
|
||||
#where_clause
|
||||
{
|
||||
type BaseType = ::fayalite::bundle::Bundle;
|
||||
type MaskType = #mask_type_ident #type_generics;
|
||||
type MatchVariant = #mask_type_match_variant_ident #type_generics;
|
||||
type MatchActiveScope = ();
|
||||
type MatchVariantAndInactiveScope = ::fayalite::ty::MatchVariantWithoutScope<
|
||||
<Self as ::fayalite::ty::Type>::MatchVariant,
|
||||
>;
|
||||
type MatchVariantsIter = ::fayalite::__std::iter::Once<
|
||||
<Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope,
|
||||
>;
|
||||
fn match_variants(
|
||||
__this: ::fayalite::expr::Expr<Self>,
|
||||
__module_builder: &mut ::fayalite::module::ModuleBuilder<
|
||||
::fayalite::module::NormalModule,
|
||||
>,
|
||||
__source_location: ::fayalite::source_location::SourceLocation,
|
||||
) -> <Self as ::fayalite::ty::Type>::MatchVariantsIter {
|
||||
let __retval = #mask_type_match_variant_ident {
|
||||
#(#match_variant_body_fields)*
|
||||
};
|
||||
::fayalite::__std::iter::once(::fayalite::ty::MatchVariantWithoutScope(__retval))
|
||||
}
|
||||
fn mask_type(&self) -> <Self as ::fayalite::ty::Type>::MaskType {
|
||||
*self
|
||||
}
|
||||
fn canonical(&self) -> ::fayalite::ty::CanonicalType {
|
||||
::fayalite::ty::Type::canonical(&::fayalite::bundle::Bundle::new(::fayalite::bundle::BundleType::fields(self)))
|
||||
}
|
||||
#[track_caller]
|
||||
fn from_canonical(__canonical_type: ::fayalite::ty::CanonicalType) -> Self {
|
||||
let ::fayalite::ty::CanonicalType::Bundle(__bundle) = __canonical_type else {
|
||||
::fayalite::__std::panic!("expected bundle");
|
||||
};
|
||||
let __fields = ::fayalite::bundle::BundleType::fields(&__bundle);
|
||||
::fayalite::__std::assert_eq!(__fields.len(), #fields_len, "bundle has wrong number of fields");
|
||||
Self {
|
||||
#(#from_canonical_body_fields)*
|
||||
}
|
||||
}
|
||||
fn source_location() -> ::fayalite::source_location::SourceLocation {
|
||||
::fayalite::source_location::SourceLocation::caller()
|
||||
}
|
||||
}
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::fayalite::bundle::BundleType for #mask_type_ident #type_generics
|
||||
#where_clause
|
||||
{
|
||||
type Builder = #unfilled_mask_type_builder_ty;
|
||||
type FilledBuilder = #filled_mask_type_builder_ty;
|
||||
fn fields(&self) -> ::fayalite::intern::Interned<[::fayalite::bundle::BundleField]> {
|
||||
::fayalite::intern::Intern::intern(&[#(#fields_body_fields)*][..])
|
||||
}
|
||||
}
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::fayalite::ty::TypeWithDeref for #mask_type_ident #type_generics
|
||||
#where_clause
|
||||
{
|
||||
fn expr_deref(__this: &::fayalite::expr::Expr<Self>) -> &<Self as ::fayalite::ty::Type>::MatchVariant {
|
||||
let __this = *__this;
|
||||
let __retval = #mask_type_match_variant_ident {
|
||||
#(#match_variant_body_fields)*
|
||||
};
|
||||
::fayalite::intern::Interned::<_>::into_inner(::fayalite::intern::Intern::intern_sized(__retval))
|
||||
}
|
||||
}
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::fayalite::ty::Type for #target #type_generics
|
||||
#where_clause
|
||||
{
|
||||
type BaseType = ::fayalite::bundle::Bundle;
|
||||
type MaskType = #mask_type_ident #type_generics;
|
||||
type MatchVariant = #match_variant_ident #type_generics;
|
||||
type MatchActiveScope = ();
|
||||
type MatchVariantAndInactiveScope = ::fayalite::ty::MatchVariantWithoutScope<
|
||||
<Self as ::fayalite::ty::Type>::MatchVariant,
|
||||
>;
|
||||
type MatchVariantsIter = ::fayalite::__std::iter::Once<
|
||||
<Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope,
|
||||
>;
|
||||
fn match_variants(
|
||||
__this: ::fayalite::expr::Expr<Self>,
|
||||
__module_builder: &mut ::fayalite::module::ModuleBuilder<
|
||||
::fayalite::module::NormalModule,
|
||||
>,
|
||||
__source_location: ::fayalite::source_location::SourceLocation,
|
||||
) -> <Self as ::fayalite::ty::Type>::MatchVariantsIter {
|
||||
let __retval = #match_variant_ident {
|
||||
#(#match_variant_body_fields)*
|
||||
};
|
||||
::fayalite::__std::iter::once(::fayalite::ty::MatchVariantWithoutScope(__retval))
|
||||
}
|
||||
fn mask_type(&self) -> <Self as ::fayalite::ty::Type>::MaskType {
|
||||
#mask_type_ident {
|
||||
#(#mask_type_body_fields)*
|
||||
}
|
||||
}
|
||||
fn canonical(&self) -> ::fayalite::ty::CanonicalType {
|
||||
::fayalite::ty::Type::canonical(&::fayalite::bundle::Bundle::new(::fayalite::bundle::BundleType::fields(self)))
|
||||
}
|
||||
#[track_caller]
|
||||
fn from_canonical(__canonical_type: ::fayalite::ty::CanonicalType) -> Self {
|
||||
let ::fayalite::ty::CanonicalType::Bundle(__bundle) = __canonical_type else {
|
||||
::fayalite::__std::panic!("expected bundle");
|
||||
};
|
||||
let __fields = ::fayalite::bundle::BundleType::fields(&__bundle);
|
||||
::fayalite::__std::assert_eq!(__fields.len(), #fields_len, "bundle has wrong number of fields");
|
||||
Self {
|
||||
#(#from_canonical_body_fields)*
|
||||
}
|
||||
}
|
||||
fn source_location() -> ::fayalite::source_location::SourceLocation {
|
||||
::fayalite::source_location::SourceLocation::caller()
|
||||
}
|
||||
}
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::fayalite::bundle::BundleType for #target #type_generics
|
||||
#where_clause
|
||||
{
|
||||
type Builder = #unfilled_builder_ty;
|
||||
type FilledBuilder = #filled_builder_ty;
|
||||
fn fields(&self) -> ::fayalite::intern::Interned<[::fayalite::bundle::BundleField]> {
|
||||
::fayalite::intern::Intern::intern(&[#(#fields_body_fields)*][..])
|
||||
}
|
||||
}
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::fayalite::ty::TypeWithDeref for #target #type_generics
|
||||
#where_clause
|
||||
{
|
||||
fn expr_deref(__this: &::fayalite::expr::Expr<Self>) -> &<Self as ::fayalite::ty::Type>::MatchVariant {
|
||||
let __this = *__this;
|
||||
let __retval = #match_variant_ident {
|
||||
#(#match_variant_body_fields)*
|
||||
};
|
||||
::fayalite::intern::Interned::<_>::into_inner(::fayalite::intern::Intern::intern_sized(__retval))
|
||||
}
|
||||
}
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
if let (None, MaybeParsed::Parsed(generics)) = (no_static, &self.generics) {
|
||||
let static_generics = generics.clone().for_static_type();
|
||||
let (static_impl_generics, static_type_generics, static_where_clause) =
|
||||
static_generics.split_for_impl();
|
||||
let static_type_body_fields = Vec::from_iter(fields.named().into_iter().map(|field| {
|
||||
let ident: &Ident = field.ident().as_ref().unwrap();
|
||||
let ty = field.ty();
|
||||
quote_spanned! {ident.span()=>
|
||||
#ident: <#ty as ::fayalite::ty::StaticType>::TYPE,
|
||||
}
|
||||
}));
|
||||
let static_mask_type_body_fields =
|
||||
Vec::from_iter(fields.named().into_iter().map(|field| {
|
||||
let ident: &Ident = field.ident().as_ref().unwrap();
|
||||
let ty = field.ty();
|
||||
quote_spanned! {ident.span()=>
|
||||
#ident: <#ty as ::fayalite::ty::StaticType>::MASK_TYPE,
|
||||
}
|
||||
}));
|
||||
let type_properties = format_ident!("__type_properties", span = ident.span());
|
||||
let type_properties_fields = Vec::from_iter(fields.named().into_iter().zip(field_flips).map(|(field, field_flip)| {
|
||||
let ident: &Ident = field.ident().as_ref().unwrap();
|
||||
let flipped = field_flip.is_some();
|
||||
let ty = field.ty();
|
||||
quote_spanned! {ident.span()=>
|
||||
let #type_properties = #type_properties.field(#flipped, <#ty as ::fayalite::ty::StaticType>::TYPE_PROPERTIES);
|
||||
}
|
||||
}));
|
||||
let type_properties_mask_fields = Vec::from_iter(fields.named().into_iter().zip(field_flips).map(|(field, field_flip)| {
|
||||
let ident: &Ident = field.ident().as_ref().unwrap();
|
||||
let flipped = field_flip.is_some();
|
||||
let ty = field.ty();
|
||||
quote_spanned! {ident.span()=>
|
||||
let #type_properties = #type_properties.field(#flipped, <#ty as ::fayalite::ty::StaticType>::MASK_TYPE_PROPERTIES);
|
||||
}
|
||||
}));
|
||||
quote_spanned! {ident.span()=>
|
||||
#[automatically_derived]
|
||||
impl #static_impl_generics ::fayalite::ty::StaticType for #mask_type_ident #static_type_generics
|
||||
#static_where_clause
|
||||
{
|
||||
const TYPE: Self = Self {
|
||||
#(#static_mask_type_body_fields)*
|
||||
};
|
||||
const MASK_TYPE: <Self as ::fayalite::ty::Type>::MaskType = Self {
|
||||
#(#static_mask_type_body_fields)*
|
||||
};
|
||||
const TYPE_PROPERTIES: ::fayalite::ty::TypeProperties = {
|
||||
let #type_properties = ::fayalite::bundle::BundleTypePropertiesBuilder::new();
|
||||
#(#type_properties_mask_fields)*
|
||||
#type_properties.finish()
|
||||
};
|
||||
const MASK_TYPE_PROPERTIES: ::fayalite::ty::TypeProperties = {
|
||||
let #type_properties = ::fayalite::bundle::BundleTypePropertiesBuilder::new();
|
||||
#(#type_properties_mask_fields)*
|
||||
#type_properties.finish()
|
||||
};
|
||||
}
|
||||
#[automatically_derived]
|
||||
impl #static_impl_generics ::fayalite::ty::StaticType for #target #static_type_generics
|
||||
#static_where_clause
|
||||
{
|
||||
const TYPE: Self = Self {
|
||||
#(#static_type_body_fields)*
|
||||
};
|
||||
const MASK_TYPE: <Self as ::fayalite::ty::Type>::MaskType = #mask_type_ident {
|
||||
#(#static_mask_type_body_fields)*
|
||||
};
|
||||
const TYPE_PROPERTIES: ::fayalite::ty::TypeProperties = {
|
||||
let #type_properties = ::fayalite::bundle::BundleTypePropertiesBuilder::new();
|
||||
#(#type_properties_fields)*
|
||||
#type_properties.finish()
|
||||
};
|
||||
const MASK_TYPE_PROPERTIES: ::fayalite::ty::TypeProperties = {
|
||||
let #type_properties = ::fayalite::bundle::BundleTypePropertiesBuilder::new();
|
||||
#(#type_properties_mask_fields)*
|
||||
#type_properties.finish()
|
||||
};
|
||||
}
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn hdl_bundle(item: ItemStruct) -> syn::Result<TokenStream> {
|
||||
let item = ParsedBundle::parse(item)?;
|
||||
let outline_generated = item.options.body.outline_generated;
|
||||
let mut contents = item.to_token_stream();
|
||||
if outline_generated.is_some() {
|
||||
contents = crate::outline_generated(contents, "hdl-bundle-");
|
||||
}
|
||||
Ok(contents)
|
||||
}
|
657
crates/fayalite-proc-macros-impl/src/hdl_enum.rs
Normal file
657
crates/fayalite-proc-macros-impl/src/hdl_enum.rs
Normal file
|
@ -0,0 +1,657 @@
|
|||
use crate::{
|
||||
hdl_type_common::{
|
||||
common_derives, get_target, ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics,
|
||||
ParsedType, SplitForImpl, TypesParser, WrappedInConst,
|
||||
},
|
||||
Errors, HdlAttr, PairsIterExt,
|
||||
};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote_spanned, ToTokens};
|
||||
use syn::{
|
||||
parse_quote_spanned,
|
||||
punctuated::{Pair, Punctuated},
|
||||
token::{Brace, Paren},
|
||||
Attribute, Field, FieldMutability, Fields, FieldsNamed, FieldsUnnamed, Generics, Ident,
|
||||
ItemEnum, ItemStruct, Token, Type, Variant, Visibility,
|
||||
};
|
||||
|
||||
crate::options! {
|
||||
#[options = VariantOptions]
|
||||
pub(crate) enum VariantOption {}
|
||||
}
|
||||
|
||||
crate::options! {
|
||||
#[options = FieldOptions]
|
||||
pub(crate) enum FieldOption {}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct ParsedVariantField {
|
||||
pub(crate) paren_token: Paren,
|
||||
pub(crate) attrs: Vec<Attribute>,
|
||||
pub(crate) options: HdlAttr<FieldOptions>,
|
||||
pub(crate) ty: MaybeParsed<ParsedType, Type>,
|
||||
pub(crate) comma_token: Option<Token![,]>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct ParsedVariant {
|
||||
pub(crate) attrs: Vec<Attribute>,
|
||||
pub(crate) options: HdlAttr<VariantOptions>,
|
||||
pub(crate) ident: Ident,
|
||||
pub(crate) field: Option<ParsedVariantField>,
|
||||
}
|
||||
|
||||
impl ParsedVariant {
|
||||
fn parse(
|
||||
errors: &mut Errors,
|
||||
variant: Variant,
|
||||
generics: &MaybeParsed<ParsedGenerics, Generics>,
|
||||
) -> Self {
|
||||
let Variant {
|
||||
mut attrs,
|
||||
ident,
|
||||
fields,
|
||||
discriminant,
|
||||
} = variant;
|
||||
let options = errors
|
||||
.unwrap_or_default(HdlAttr::parse_and_take_attr(&mut attrs))
|
||||
.unwrap_or_default();
|
||||
let field = match fields {
|
||||
Fields::Unnamed(FieldsUnnamed {
|
||||
paren_token,
|
||||
unnamed,
|
||||
}) if unnamed.len() == 1 => {
|
||||
let (field, comma_token) = unnamed.into_pairs().next().unwrap().into_tuple();
|
||||
let Field {
|
||||
mut attrs,
|
||||
vis,
|
||||
mutability,
|
||||
ident: _,
|
||||
colon_token: _,
|
||||
ty,
|
||||
} = field;
|
||||
let options = errors
|
||||
.unwrap_or_default(HdlAttr::parse_and_take_attr(&mut attrs))
|
||||
.unwrap_or_default();
|
||||
if !matches!(vis, Visibility::Inherited) {
|
||||
errors.error(
|
||||
&vis,
|
||||
"enum variant fields must not have a visibility specifier",
|
||||
);
|
||||
}
|
||||
if !matches!(mutability, FieldMutability::None) {
|
||||
// FIXME: use mutability as the spanned tokens,
|
||||
// blocked on https://github.com/dtolnay/syn/issues/1717
|
||||
errors.error(&ty, "field mutability is not supported");
|
||||
}
|
||||
Some(ParsedVariantField {
|
||||
paren_token,
|
||||
attrs,
|
||||
options,
|
||||
ty: TypesParser::maybe_run(generics.as_ref(), ty, errors),
|
||||
comma_token,
|
||||
})
|
||||
}
|
||||
Fields::Unit => None,
|
||||
Fields::Unnamed(fields) if fields.unnamed.is_empty() => None,
|
||||
Fields::Named(fields) if fields.named.is_empty() => None,
|
||||
Fields::Unnamed(_) | Fields::Named(_) => {
|
||||
errors.error(
|
||||
fields,
|
||||
"enum variant must either have no fields or a single parenthesized field",
|
||||
);
|
||||
None
|
||||
}
|
||||
};
|
||||
if let Some((eq, _)) = discriminant {
|
||||
errors.error(eq, "custom enum discriminants are not allowed");
|
||||
}
|
||||
Self {
|
||||
attrs,
|
||||
options,
|
||||
ident,
|
||||
field,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct ParsedEnum {
|
||||
pub(crate) attrs: Vec<Attribute>,
|
||||
pub(crate) options: HdlAttr<ItemOptions>,
|
||||
pub(crate) vis: Visibility,
|
||||
pub(crate) enum_token: Token![enum],
|
||||
pub(crate) ident: Ident,
|
||||
pub(crate) generics: MaybeParsed<ParsedGenerics, Generics>,
|
||||
pub(crate) brace_token: Brace,
|
||||
pub(crate) variants: Punctuated<ParsedVariant, Token![,]>,
|
||||
pub(crate) match_variant_ident: Ident,
|
||||
}
|
||||
|
||||
impl ParsedEnum {
|
||||
fn parse(item: ItemEnum) -> syn::Result<Self> {
|
||||
let ItemEnum {
|
||||
mut attrs,
|
||||
vis,
|
||||
enum_token,
|
||||
ident,
|
||||
mut generics,
|
||||
brace_token,
|
||||
variants,
|
||||
} = item;
|
||||
let mut errors = Errors::new();
|
||||
let mut options = errors
|
||||
.unwrap_or_default(HdlAttr::<ItemOptions>::parse_and_take_attr(&mut attrs))
|
||||
.unwrap_or_default();
|
||||
errors.ok(options.body.validate());
|
||||
let ItemOptions {
|
||||
outline_generated: _,
|
||||
connect_inexact: _,
|
||||
target: _,
|
||||
custom_bounds,
|
||||
no_static: _,
|
||||
} = options.body;
|
||||
attrs.retain(|attr| {
|
||||
if attr.path().is_ident("repr") {
|
||||
errors.error(attr, "#[repr] is not supported on #[hdl] enums");
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
let generics = if custom_bounds.is_some() {
|
||||
MaybeParsed::Unrecognized(generics)
|
||||
} else if let Some(generics) = errors.ok(ParsedGenerics::parse(&mut generics)) {
|
||||
MaybeParsed::Parsed(generics)
|
||||
} else {
|
||||
MaybeParsed::Unrecognized(generics)
|
||||
};
|
||||
let variants = Punctuated::from_iter(
|
||||
variants
|
||||
.into_pairs()
|
||||
.map_pair_value(|v| ParsedVariant::parse(&mut errors, v, &generics)),
|
||||
);
|
||||
errors.finish()?;
|
||||
Ok(Self {
|
||||
attrs,
|
||||
options,
|
||||
vis,
|
||||
enum_token,
|
||||
generics,
|
||||
brace_token,
|
||||
variants,
|
||||
match_variant_ident: format_ident!("__{}__MatchVariant", ident),
|
||||
ident,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for ParsedEnum {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let Self {
|
||||
attrs,
|
||||
options,
|
||||
vis,
|
||||
enum_token,
|
||||
ident,
|
||||
generics,
|
||||
brace_token,
|
||||
variants,
|
||||
match_variant_ident,
|
||||
} = self;
|
||||
let ItemOptions {
|
||||
outline_generated: _,
|
||||
connect_inexact,
|
||||
target,
|
||||
custom_bounds: _,
|
||||
no_static,
|
||||
} = &options.body;
|
||||
let target = get_target(target, ident);
|
||||
let mut struct_attrs = attrs.clone();
|
||||
struct_attrs.push(common_derives(ident.span()));
|
||||
struct_attrs.push(parse_quote_spanned! {ident.span()=>
|
||||
#[allow(non_snake_case)]
|
||||
});
|
||||
let struct_fields = Punctuated::from_iter(variants.pairs().map_pair_value_ref(
|
||||
|ParsedVariant {
|
||||
attrs: _,
|
||||
options,
|
||||
ident,
|
||||
field,
|
||||
}| {
|
||||
let VariantOptions {} = options.body;
|
||||
let colon_token;
|
||||
let ty = if let Some(ParsedVariantField {
|
||||
paren_token,
|
||||
attrs: _,
|
||||
options,
|
||||
ty,
|
||||
comma_token: _,
|
||||
}) = field
|
||||
{
|
||||
let FieldOptions {} = options.body;
|
||||
colon_token = Token);
|
||||
ty.clone().into()
|
||||
} else {
|
||||
colon_token = Token);
|
||||
parse_quote_spanned! {ident.span()=>
|
||||
()
|
||||
}
|
||||
};
|
||||
Field {
|
||||
attrs: vec![],
|
||||
vis: vis.clone(),
|
||||
mutability: FieldMutability::None,
|
||||
ident: Some(ident.clone()),
|
||||
colon_token: Some(colon_token),
|
||||
ty,
|
||||
}
|
||||
},
|
||||
));
|
||||
ItemStruct {
|
||||
attrs: struct_attrs,
|
||||
vis: vis.clone(),
|
||||
struct_token: Token,
|
||||
ident: ident.clone(),
|
||||
generics: generics.into(),
|
||||
fields: if struct_fields.is_empty() {
|
||||
Fields::Unit
|
||||
} else {
|
||||
Fields::Named(FieldsNamed {
|
||||
brace_token: *brace_token,
|
||||
named: struct_fields,
|
||||
})
|
||||
},
|
||||
semi_token: None,
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
|
||||
if let MaybeParsed::Parsed(generics) = generics {
|
||||
generics.make_runtime_generics(tokens, vis, ident, &target, |context| {
|
||||
let fields: Vec<_> = variants
|
||||
.iter()
|
||||
.map(|ParsedVariant { ident, field, .. }| {
|
||||
if let Some(ParsedVariantField {
|
||||
ty: MaybeParsed::Parsed(ty),
|
||||
..
|
||||
}) = field
|
||||
{
|
||||
let expr = ty.make_hdl_type_expr(context);
|
||||
quote_spanned! {ident.span()=>
|
||||
#ident: #expr,
|
||||
}
|
||||
} else {
|
||||
quote_spanned! {ident.span()=>
|
||||
#ident: (),
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
parse_quote_spanned! {ident.span()=>
|
||||
#target {
|
||||
#(#fields)*
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
let mut wrapped_in_const = WrappedInConst::new(tokens, ident.span());
|
||||
let tokens = wrapped_in_const.inner();
|
||||
{
|
||||
let mut wrapped_in_const = WrappedInConst::new(tokens, ident.span());
|
||||
let tokens = wrapped_in_const.inner();
|
||||
let mut enum_attrs = attrs.clone();
|
||||
enum_attrs.push(parse_quote_spanned! {ident.span()=>
|
||||
#[allow(dead_code)]
|
||||
});
|
||||
ItemEnum {
|
||||
attrs: enum_attrs,
|
||||
vis: vis.clone(),
|
||||
enum_token: *enum_token,
|
||||
ident: ident.clone(),
|
||||
generics: generics.into(),
|
||||
brace_token: *brace_token,
|
||||
variants: Punctuated::from_iter(variants.pairs().map_pair_value_ref(
|
||||
|ParsedVariant {
|
||||
attrs,
|
||||
options: _,
|
||||
ident,
|
||||
field,
|
||||
}| Variant {
|
||||
attrs: attrs.clone(),
|
||||
ident: ident.clone(),
|
||||
fields: match field {
|
||||
Some(ParsedVariantField {
|
||||
paren_token,
|
||||
attrs,
|
||||
options: _,
|
||||
ty,
|
||||
comma_token,
|
||||
}) => Fields::Unnamed(FieldsUnnamed {
|
||||
paren_token: *paren_token,
|
||||
unnamed: Punctuated::from_iter([Pair::new(
|
||||
Field {
|
||||
attrs: attrs.clone(),
|
||||
vis: Visibility::Inherited,
|
||||
mutability: FieldMutability::None,
|
||||
ident: None,
|
||||
colon_token: None,
|
||||
ty: ty.clone().into(),
|
||||
},
|
||||
*comma_token,
|
||||
)]),
|
||||
}),
|
||||
None => Fields::Unit,
|
||||
},
|
||||
discriminant: None,
|
||||
},
|
||||
)),
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
}
|
||||
let mut enum_attrs = attrs.clone();
|
||||
enum_attrs.push(parse_quote_spanned! {ident.span()=>
|
||||
#[allow(dead_code, non_camel_case_types)]
|
||||
});
|
||||
ItemEnum {
|
||||
attrs: enum_attrs,
|
||||
vis: vis.clone(),
|
||||
enum_token: *enum_token,
|
||||
ident: match_variant_ident.clone(),
|
||||
generics: generics.into(),
|
||||
brace_token: *brace_token,
|
||||
variants: Punctuated::from_iter(variants.pairs().map_pair_value_ref(
|
||||
|ParsedVariant {
|
||||
attrs,
|
||||
options: _,
|
||||
ident,
|
||||
field,
|
||||
}| Variant {
|
||||
attrs: attrs.clone(),
|
||||
ident: ident.clone(),
|
||||
fields: match field {
|
||||
Some(ParsedVariantField {
|
||||
paren_token,
|
||||
attrs,
|
||||
options: _,
|
||||
ty,
|
||||
comma_token,
|
||||
}) => Fields::Unnamed(FieldsUnnamed {
|
||||
paren_token: *paren_token,
|
||||
unnamed: Punctuated::from_iter([Pair::new(
|
||||
Field {
|
||||
attrs: attrs.clone(),
|
||||
vis: Visibility::Inherited,
|
||||
mutability: FieldMutability::None,
|
||||
ident: None,
|
||||
colon_token: None,
|
||||
ty: parse_quote_spanned! {ident.span()=>
|
||||
::fayalite::expr::Expr<#ty>
|
||||
},
|
||||
},
|
||||
*comma_token,
|
||||
)]),
|
||||
}),
|
||||
None => Fields::Unit,
|
||||
},
|
||||
discriminant: None,
|
||||
},
|
||||
)),
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
for (index, ParsedVariant { ident, field, .. }) in variants.iter().enumerate() {
|
||||
if let Some(ParsedVariantField { ty, .. }) = field {
|
||||
quote_spanned! {ident.span()=>
|
||||
#[automatically_derived]
|
||||
impl #impl_generics #target #type_generics
|
||||
#where_clause
|
||||
{
|
||||
#[allow(non_snake_case, dead_code)]
|
||||
#vis fn #ident<__V: ::fayalite::expr::ToExpr<Type = #ty>>(
|
||||
self,
|
||||
v: __V,
|
||||
) -> ::fayalite::expr::Expr<Self> {
|
||||
::fayalite::expr::ToExpr::to_expr(
|
||||
&::fayalite::expr::ops::EnumLiteral::new(
|
||||
self,
|
||||
#index,
|
||||
::fayalite::__std::option::Option::Some(
|
||||
::fayalite::expr::Expr::canonical(
|
||||
::fayalite::expr::ToExpr::to_expr(&v),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote_spanned! {ident.span()=>
|
||||
#[automatically_derived]
|
||||
impl #impl_generics #target #type_generics
|
||||
#where_clause
|
||||
{
|
||||
#[allow(non_snake_case, dead_code)]
|
||||
#vis fn #ident(self) -> ::fayalite::expr::Expr<Self> {
|
||||
::fayalite::expr::ToExpr::to_expr(
|
||||
&::fayalite::expr::ops::EnumLiteral::new(
|
||||
self,
|
||||
#index,
|
||||
::fayalite::__std::option::Option::None,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
}
|
||||
let from_canonical_body_fields = Vec::from_iter(variants.iter().enumerate().map(
|
||||
|(index, ParsedVariant { ident, field, .. })| {
|
||||
let ident_str = ident.to_string();
|
||||
let val = if field.is_some() {
|
||||
let missing_value_msg = format!("expected variant {ident} to have a field");
|
||||
quote_spanned! {ident.span()=>
|
||||
::fayalite::ty::Type::from_canonical(ty.expect(#missing_value_msg))
|
||||
}
|
||||
} else {
|
||||
quote_spanned! {ident.span()=>
|
||||
::fayalite::__std::assert!(ty.is_none());
|
||||
}
|
||||
};
|
||||
quote_spanned! {ident.span()=>
|
||||
#ident: {
|
||||
let ::fayalite::enum_::EnumVariant {
|
||||
name,
|
||||
ty,
|
||||
} = variants[#index];
|
||||
::fayalite::__std::assert_eq!(&*name, #ident_str);
|
||||
#val
|
||||
},
|
||||
}
|
||||
},
|
||||
));
|
||||
let match_active_scope_match_arms = Vec::from_iter(variants.iter().enumerate().map(
|
||||
|(index, ParsedVariant { ident, field, .. })| {
|
||||
if field.is_some() {
|
||||
quote_spanned! {ident.span()=>
|
||||
#index => #match_variant_ident::#ident(
|
||||
::fayalite::expr::ToExpr::to_expr(
|
||||
&::fayalite::expr::ops::VariantAccess::new_unchecked(
|
||||
variant_access.base(),
|
||||
variant_access.variant_index(),
|
||||
),
|
||||
),
|
||||
),
|
||||
}
|
||||
} else {
|
||||
quote_spanned! {ident.span()=>
|
||||
#index => #match_variant_ident::#ident,
|
||||
}
|
||||
}
|
||||
},
|
||||
));
|
||||
let variants_body_variants = Vec::from_iter(variants.iter().map(
|
||||
|ParsedVariant {
|
||||
attrs: _,
|
||||
options,
|
||||
ident,
|
||||
field,
|
||||
}| {
|
||||
let VariantOptions {} = options.body;
|
||||
let ident_str = ident.to_string();
|
||||
match field {
|
||||
Some(ParsedVariantField { options, .. }) => {
|
||||
let FieldOptions {} = options.body;
|
||||
quote_spanned! {ident.span()=>
|
||||
::fayalite::enum_::EnumVariant {
|
||||
name: ::fayalite::intern::Intern::intern(#ident_str),
|
||||
ty: ::fayalite::__std::option::Option::Some(
|
||||
::fayalite::ty::Type::canonical(&self.#ident),
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
None => quote_spanned! {ident.span()=>
|
||||
::fayalite::enum_::EnumVariant {
|
||||
name: ::fayalite::intern::Intern::intern(#ident_str),
|
||||
ty: ::fayalite::__std::option::Option::None,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
));
|
||||
let variants_len = variants.len();
|
||||
quote_spanned! {ident.span()=>
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::fayalite::ty::Type for #target #type_generics
|
||||
#where_clause
|
||||
{
|
||||
type BaseType = ::fayalite::enum_::Enum;
|
||||
type MaskType = ::fayalite::int::Bool;
|
||||
type MatchVariant = #match_variant_ident #type_generics;
|
||||
type MatchActiveScope = ::fayalite::module::Scope;
|
||||
type MatchVariantAndInactiveScope = ::fayalite::enum_::EnumMatchVariantAndInactiveScope<Self>;
|
||||
type MatchVariantsIter = ::fayalite::enum_::EnumMatchVariantsIter<Self>;
|
||||
|
||||
fn match_variants(
|
||||
this: ::fayalite::expr::Expr<Self>,
|
||||
module_builder: &mut ::fayalite::module::ModuleBuilder<::fayalite::module::NormalModule>,
|
||||
source_location: ::fayalite::source_location::SourceLocation,
|
||||
) -> <Self as ::fayalite::ty::Type>::MatchVariantsIter {
|
||||
module_builder.enum_match_variants_helper(this, source_location)
|
||||
}
|
||||
fn mask_type(&self) -> <Self as ::fayalite::ty::Type>::MaskType {
|
||||
::fayalite::int::Bool
|
||||
}
|
||||
fn canonical(&self) -> ::fayalite::ty::CanonicalType {
|
||||
::fayalite::ty::CanonicalType::Enum(::fayalite::enum_::Enum::new(::fayalite::enum_::EnumType::variants(self)))
|
||||
}
|
||||
#[track_caller]
|
||||
#[allow(non_snake_case)]
|
||||
fn from_canonical(canonical_type: ::fayalite::ty::CanonicalType) -> Self {
|
||||
let ::fayalite::ty::CanonicalType::Enum(enum_) = canonical_type else {
|
||||
::fayalite::__std::panic!("expected enum");
|
||||
};
|
||||
let variants = ::fayalite::enum_::EnumType::variants(&enum_);
|
||||
::fayalite::__std::assert_eq!(variants.len(), #variants_len, "enum has wrong number of variants");
|
||||
Self {
|
||||
#(#from_canonical_body_fields)*
|
||||
}
|
||||
}
|
||||
fn source_location() -> ::fayalite::source_location::SourceLocation {
|
||||
::fayalite::source_location::SourceLocation::caller()
|
||||
}
|
||||
}
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::fayalite::enum_::EnumType for #target #type_generics
|
||||
#where_clause
|
||||
{
|
||||
fn match_activate_scope(
|
||||
v: <Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope,
|
||||
) -> (<Self as ::fayalite::ty::Type>::MatchVariant, <Self as ::fayalite::ty::Type>::MatchActiveScope) {
|
||||
let (variant_access, scope) = v.activate();
|
||||
(
|
||||
match variant_access.variant_index() {
|
||||
#(#match_active_scope_match_arms)*
|
||||
#variants_len.. => ::fayalite::__std::panic!("invalid variant index"),
|
||||
},
|
||||
scope,
|
||||
)
|
||||
}
|
||||
fn variants(&self) -> ::fayalite::intern::Interned<[::fayalite::enum_::EnumVariant]> {
|
||||
::fayalite::intern::Intern::intern(&[
|
||||
#(#variants_body_variants)*
|
||||
][..])
|
||||
}
|
||||
}
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
if let (None, MaybeParsed::Parsed(generics)) = (no_static, &self.generics) {
|
||||
let static_generics = generics.clone().for_static_type();
|
||||
let (static_impl_generics, static_type_generics, static_where_clause) =
|
||||
static_generics.split_for_impl();
|
||||
let static_type_body_variants =
|
||||
Vec::from_iter(variants.iter().map(|ParsedVariant { ident, field, .. }| {
|
||||
if let Some(_) = field {
|
||||
quote_spanned! {ident.span()=>
|
||||
#ident: ::fayalite::ty::StaticType::TYPE,
|
||||
}
|
||||
} else {
|
||||
quote_spanned! {ident.span()=>
|
||||
#ident: (),
|
||||
}
|
||||
}
|
||||
}));
|
||||
let type_properties = format_ident!("__type_properties", span = ident.span());
|
||||
let type_properties_variants =
|
||||
Vec::from_iter(variants.iter().map(|ParsedVariant { ident, field, .. }| {
|
||||
let variant = if let Some(ParsedVariantField { ty, .. }) = field {
|
||||
quote_spanned! {ident.span()=>
|
||||
::fayalite::__std::option::Option::Some(
|
||||
<#ty as ::fayalite::ty::StaticType>::TYPE_PROPERTIES,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
quote_spanned! {ident.span()=>
|
||||
::fayalite::__std::option::Option::None
|
||||
}
|
||||
};
|
||||
quote_spanned! {ident.span()=>
|
||||
let #type_properties = #type_properties.variant(#variant);
|
||||
}
|
||||
}));
|
||||
quote_spanned! {ident.span()=>
|
||||
#[automatically_derived]
|
||||
impl #static_impl_generics ::fayalite::ty::StaticType
|
||||
for #target #static_type_generics
|
||||
#static_where_clause
|
||||
{
|
||||
const TYPE: Self = Self {
|
||||
#(#static_type_body_variants)*
|
||||
};
|
||||
const MASK_TYPE: <Self as ::fayalite::ty::Type>::MaskType =
|
||||
::fayalite::int::Bool;
|
||||
const TYPE_PROPERTIES: ::fayalite::ty::TypeProperties = {
|
||||
let #type_properties = ::fayalite::enum_::EnumTypePropertiesBuilder::new();
|
||||
#(#type_properties_variants)*
|
||||
#type_properties.finish()
|
||||
};
|
||||
const MASK_TYPE_PROPERTIES: ::fayalite::ty::TypeProperties =
|
||||
<::fayalite::int::Bool as ::fayalite::ty::StaticType>::TYPE_PROPERTIES;
|
||||
}
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn hdl_enum(item: ItemEnum) -> syn::Result<TokenStream> {
|
||||
let item = ParsedEnum::parse(item)?;
|
||||
let outline_generated = item.options.body.outline_generated;
|
||||
let mut contents = item.to_token_stream();
|
||||
if outline_generated.is_some() {
|
||||
contents = crate::outline_generated(contents, "hdl-enum-");
|
||||
}
|
||||
Ok(contents)
|
||||
}
|
2981
crates/fayalite-proc-macros-impl/src/hdl_type_common.rs
Normal file
2981
crates/fayalite-proc-macros-impl/src/hdl_type_common.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -3,7 +3,10 @@
|
|||
#![cfg_attr(test, recursion_limit = "512")]
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::{quote, ToTokens};
|
||||
use std::io::{ErrorKind, Write};
|
||||
use std::{
|
||||
convert::Infallible,
|
||||
io::{ErrorKind, Write},
|
||||
};
|
||||
use syn::{
|
||||
bracketed, parenthesized,
|
||||
parse::{Parse, ParseStream, Parser},
|
||||
|
@ -13,6 +16,9 @@ use syn::{
|
|||
};
|
||||
|
||||
mod fold;
|
||||
mod hdl_bundle;
|
||||
mod hdl_enum;
|
||||
mod hdl_type_common;
|
||||
mod module;
|
||||
mod value_derive_common;
|
||||
mod value_derive_enum;
|
||||
|
@ -43,6 +49,7 @@ mod kw {
|
|||
|
||||
custom_keyword!(clock_domain);
|
||||
custom_keyword!(connect_inexact);
|
||||
custom_keyword!(custom_bounds);
|
||||
custom_keyword!(flip);
|
||||
custom_keyword!(hdl);
|
||||
custom_keyword!(input);
|
||||
|
@ -52,6 +59,7 @@ mod kw {
|
|||
custom_keyword!(memory_array);
|
||||
custom_keyword!(memory_with_init);
|
||||
custom_keyword!(no_reset);
|
||||
custom_keyword!(no_static);
|
||||
custom_keyword!(outline_generated);
|
||||
custom_keyword!(output);
|
||||
custom_keyword!(reg_builder);
|
||||
|
@ -460,6 +468,15 @@ impl Errors {
|
|||
self.push(Error::new_spanned(tokens, message));
|
||||
self
|
||||
}
|
||||
pub(crate) fn fatal_error(
|
||||
mut self,
|
||||
tokens: impl ToTokens,
|
||||
message: impl std::fmt::Display,
|
||||
) -> syn::Result<Infallible> {
|
||||
self.push(Error::new_spanned(tokens, message));
|
||||
self.finish()?;
|
||||
unreachable!()
|
||||
}
|
||||
pub(crate) fn ok<T>(&mut self, v: syn::Result<T>) -> Option<T> {
|
||||
match v {
|
||||
Ok(v) => Some(v),
|
||||
|
@ -519,6 +536,26 @@ macro_rules! impl_extra_traits_for_options {
|
|||
) => {
|
||||
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;
|
||||
|
@ -554,6 +591,66 @@ 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)?),)*
|
||||
|
@ -567,8 +664,11 @@ macro_rules! options {
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
#[allow(non_snake_case)]
|
||||
$enum_vis struct $options_name {
|
||||
$($enum_vis $key: Option<(crate::kw::$key, $(syn::token::Paren, $value)?)>,)*
|
||||
$(
|
||||
$enum_vis $key: Option<(crate::kw::$key, $(syn::token::Paren, $value)?)>,
|
||||
)*
|
||||
}
|
||||
|
||||
crate::fold::impl_fold! {
|
||||
|
@ -577,6 +677,43 @@ macro_rules! options {
|
|||
}
|
||||
}
|
||||
|
||||
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)]
|
||||
|
@ -585,7 +722,7 @@ macro_rules! options {
|
|||
let old_input = input.fork();
|
||||
match input.parse::<$option_enum_name>()? {
|
||||
$($option_enum_name::$Variant(v) => {
|
||||
if retval.$key.replace(v).is_some() {
|
||||
if retval.$key.replace(v).is_some() && !$allow_duplicates {
|
||||
return Err(old_input.error(concat!("duplicate ", stringify!($key), " option")));
|
||||
}
|
||||
})*
|
||||
|
@ -593,7 +730,7 @@ macro_rules! options {
|
|||
if input.is_empty() {
|
||||
break;
|
||||
}
|
||||
input.parse::<syn::Token![,]>()?;
|
||||
input.parse::<$Punct>()?;
|
||||
}
|
||||
Ok(retval)
|
||||
}
|
||||
|
@ -602,7 +739,7 @@ macro_rules! options {
|
|||
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;
|
||||
let mut separator: Option<$Punct> = None;
|
||||
$(if let Some(v) = &self.$key {
|
||||
separator.to_tokens(tokens);
|
||||
separator = Some(Default::default());
|
||||
|
@ -673,6 +810,20 @@ macro_rules! options {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,)*
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -686,6 +837,15 @@ pub(crate) fn outline_generated(contents: TokenStream, prefix: &str) -> TokenStr
|
|||
.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]);
|
||||
|
@ -728,3 +888,15 @@ pub fn value_derive(item: TokenStream) -> syn::Result<TokenStream> {
|
|||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hdl_attr(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> {
|
||||
let item = syn::parse2::<Item>(quote! { #[hdl(#attr)] #item })?;
|
||||
match item {
|
||||
Item::Enum(item) => hdl_enum::hdl_enum(item),
|
||||
Item::Struct(item) => hdl_bundle::hdl_bundle(item),
|
||||
_ => Err(syn::Error::new(
|
||||
Span::call_site(),
|
||||
"top-level #[hdl] can only be used on structs or enums",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1290,7 +1290,10 @@ impl Visitor {
|
|||
memory,
|
||||
paren,
|
||||
ty_expr,
|
||||
} => (paren, unwrap_or_static_type(ty_expr.as_ref(), memory.span())),
|
||||
} => (
|
||||
paren,
|
||||
unwrap_or_static_type(ty_expr.as_ref(), memory.span()),
|
||||
),
|
||||
MemoryFn::MemoryArray {
|
||||
memory_array,
|
||||
paren,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue