339 lines
14 KiB
Rust
339 lines
14 KiB
Rust
use crate::{
|
|
hdl_type_common::{get_target, type_derives, TypeOptions, WrappedInConst},
|
|
kw, Errors, HdlAttr,
|
|
};
|
|
use proc_macro2::TokenStream;
|
|
use quote::{format_ident, quote_spanned, ToTokens};
|
|
use syn::{
|
|
parse_quote_spanned, punctuated::Punctuated, spanned::Spanned, token::Brace, Attribute, Field,
|
|
Fields, FieldsNamed, Generics, Ident, ItemStruct, Token, Visibility,
|
|
};
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct ParsedBundle {
|
|
pub(crate) attrs: Vec<Attribute>,
|
|
pub(crate) options: HdlAttr<TypeOptions>,
|
|
pub(crate) vis: Visibility,
|
|
pub(crate) struct_token: Token![struct],
|
|
pub(crate) ident: Ident,
|
|
pub(crate) generics: Generics,
|
|
pub(crate) fields: 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,
|
|
}
|
|
|
|
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()));
|
|
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,
|
|
generics,
|
|
fields,
|
|
semi_token,
|
|
} = item;
|
|
let mut errors = Errors::new();
|
|
let options = errors
|
|
.unwrap_or_default(HdlAttr::parse_and_take_attr(&mut attrs))
|
|
.unwrap_or_default();
|
|
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));
|
|
}
|
|
errors.finish()?;
|
|
Ok(Self {
|
|
attrs,
|
|
options,
|
|
vis,
|
|
struct_token,
|
|
generics,
|
|
fields,
|
|
field_flips,
|
|
mask_type_ident: format_ident!("__{ident}__MaskType"),
|
|
mask_type_match_variant_ident: format_ident!("__{ident}__MaskType__MatchVariant"),
|
|
match_variant_ident: format_ident!("__{ident}__MatchVariant"),
|
|
ident,
|
|
})
|
|
}
|
|
}
|
|
|
|
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,
|
|
} = self;
|
|
let TypeOptions {
|
|
outline_generated: _,
|
|
connect_inexact,
|
|
target,
|
|
} = &options.body;
|
|
let target = get_target(target, ident);
|
|
let mut item_attrs = attrs.clone();
|
|
item_attrs.push(type_derives(ident.span()));
|
|
ItemStruct {
|
|
attrs: item_attrs,
|
|
vis: vis.clone(),
|
|
struct_token: *struct_token,
|
|
ident: ident.clone(),
|
|
generics: generics.clone(),
|
|
fields: Fields::Named(fields.clone()),
|
|
semi_token: None,
|
|
}
|
|
.to_tokens(tokens);
|
|
let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
|
|
let mut wrapped_in_const = WrappedInConst::new(tokens, ident.span());
|
|
let tokens = wrapped_in_const.inner();
|
|
let mut mask_type_fields = 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
|
|
};
|
|
}
|
|
ItemStruct {
|
|
attrs: vec![
|
|
parse_quote_spanned! {ident.span()=>
|
|
#[allow(non_camel_case_types)]
|
|
},
|
|
type_derives(ident.span()),
|
|
],
|
|
vis: vis.clone(),
|
|
struct_token: *struct_token,
|
|
ident: mask_type_ident.clone(),
|
|
generics: generics.clone(),
|
|
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![parse_quote_spanned! {ident.span()=>
|
|
#[allow(non_camel_case_types)]
|
|
}],
|
|
vis: vis.clone(),
|
|
struct_token: *struct_token,
|
|
ident: mask_type_match_variant_ident.clone(),
|
|
generics: generics.clone(),
|
|
fields: Fields::Named(mask_type_match_variant_fields),
|
|
semi_token: None,
|
|
}
|
|
.to_tokens(tokens);
|
|
let mut match_variant_fields = 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![parse_quote_spanned! {ident.span()=>
|
|
#[allow(non_camel_case_types)]
|
|
}],
|
|
vis: vis.clone(),
|
|
struct_token: *struct_token,
|
|
ident: match_variant_ident.clone(),
|
|
generics: generics.clone(),
|
|
fields: Fields::Named(match_variant_fields),
|
|
semi_token: None,
|
|
}
|
|
.to_tokens(tokens);
|
|
let match_variant_body_fields =
|
|
Vec::from_iter(fields.named.iter().map(|Field { ident, .. }| {
|
|
let ident: &Ident = 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.iter().map(|Field { ident, .. }| {
|
|
let ident: &Ident = 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.iter().enumerate().zip(field_flips).map(
|
|
|((index, Field { ident, .. }), flip)| {
|
|
let ident: &Ident = 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, flipped, ty } = fields[#index];
|
|
::fayalite::__std::assert_eq!(&*name, #ident_str);
|
|
::fayalite::__std::assert_eq!(flipped, #flipped);
|
|
::fayalite::ty::Type::from_canonical(ty)
|
|
},
|
|
}
|
|
},
|
|
));
|
|
let fields_len = fields.named.len();
|
|
quote_spanned! {ident.span()=>
|
|
#[automatically_derived]
|
|
impl #impl_generics ::fayalite::ty::Type for #mask_type_ident #type_generics
|
|
#where_clause
|
|
{
|
|
type MaskType = #mask_type_ident #type_generics;
|
|
type MatchVariant = #mask_type_match_variant_ident #type_generics;
|
|
type MatchActiveScope = ();
|
|
type MatchVariantAndInactiveScope = 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 _ = this;
|
|
let _ = module_builder;
|
|
let _ = source_location;
|
|
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::ty::Type for #target #type_generics
|
|
#where_clause
|
|
{
|
|
type MaskType = #mask_type_ident #type_generics;
|
|
type MatchVariant = #match_variant_ident #type_generics;
|
|
type MatchActiveScope = ();
|
|
type MatchVariantAndInactiveScope = 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 _ = this;
|
|
let _ = module_builder;
|
|
let _ = source_location;
|
|
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 {
|
|
#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()
|
|
}
|
|
}
|
|
}
|
|
.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)
|
|
}
|