WIP: use HdlOption[the_type_var] or UInt[123 + n] for creating types
All checks were successful
/ test (push) Successful in 14s
All checks were successful
/ test (push) Successful in 14s
This commit is contained in:
parent
cd99dbc849
commit
73d80cadf8
18 changed files with 2734 additions and 6895 deletions
339
crates/fayalite-proc-macros-impl/src/hdl_bundle.rs
Normal file
339
crates/fayalite-proc-macros-impl/src/hdl_bundle.rs
Normal file
|
@ -0,0 +1,339 @@
|
|||
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)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue