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)
|
||||
}
|
6
crates/fayalite-proc-macros-impl/src/hdl_enum.rs
Normal file
6
crates/fayalite-proc-macros-impl/src/hdl_enum.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
use proc_macro2::TokenStream;
|
||||
use syn::ItemEnum;
|
||||
|
||||
pub(crate) fn hdl_enum(item: ItemEnum) -> syn::Result<TokenStream> {
|
||||
todo!()
|
||||
}
|
70
crates/fayalite-proc-macros-impl/src/hdl_type_common.rs
Normal file
70
crates/fayalite-proc-macros-impl/src/hdl_type_common.rs
Normal file
|
@ -0,0 +1,70 @@
|
|||
use crate::{fold::impl_fold, kw};
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::{quote, quote_spanned, ToTokens};
|
||||
use syn::{
|
||||
parse::{Parse, ParseStream},
|
||||
parse_quote_spanned,
|
||||
punctuated::Punctuated,
|
||||
token::Paren,
|
||||
Attribute, Ident, Path, Token, WhereClause, WherePredicate,
|
||||
};
|
||||
|
||||
crate::options! {
|
||||
#[options = TypeOptions]
|
||||
pub(crate) enum TypeOption {
|
||||
OutlineGenerated(outline_generated),
|
||||
ConnectInexact(connect_inexact),
|
||||
Target(target, Path),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct WrappedInConst<'a> {
|
||||
outer: &'a mut TokenStream,
|
||||
span: Span,
|
||||
inner: TokenStream,
|
||||
}
|
||||
|
||||
impl<'a> WrappedInConst<'a> {
|
||||
pub(crate) fn new(outer: &'a mut TokenStream, span: Span) -> Self {
|
||||
Self {
|
||||
outer,
|
||||
span,
|
||||
inner: TokenStream::new(),
|
||||
}
|
||||
}
|
||||
pub(crate) fn inner(&mut self) -> &mut TokenStream {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for WrappedInConst<'_> {
|
||||
fn drop(&mut self) {
|
||||
let inner = &self.inner;
|
||||
quote_spanned! {self.span=>
|
||||
const _: () = {
|
||||
#inner
|
||||
};
|
||||
}
|
||||
.to_tokens(self.outer);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_target(target: &Option<(kw::target, Paren, Path)>, item_ident: &Ident) -> Path {
|
||||
match target {
|
||||
Some((_, _, target)) => target.clone(),
|
||||
None => item_ident.clone().into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn type_derives(span: Span) -> Attribute {
|
||||
parse_quote_spanned! {span=>
|
||||
#[::fayalite::__std::prelude::v1::derive(
|
||||
::fayalite::__std::fmt::Debug,
|
||||
::fayalite::__std::cmp::Eq,
|
||||
::fayalite::__std::cmp::PartialEq,
|
||||
::fayalite::__std::hash::Hash,
|
||||
::fayalite::__std::marker::Copy,
|
||||
::fayalite::__std::clone::Clone,
|
||||
)]
|
||||
}
|
||||
}
|
|
@ -13,6 +13,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;
|
||||
|
@ -728,3 +731,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