forked from libre-chip/fayalite
initial public commit
This commit is contained in:
commit
0b958e7852
56 changed files with 30235 additions and 0 deletions
282
crates/fayalite-proc-macros-impl/src/module.rs
Normal file
282
crates/fayalite-proc-macros-impl/src/module.rs
Normal file
|
@ -0,0 +1,282 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use crate::{
|
||||
is_hdl_attr,
|
||||
module::transform_body::{HdlLet, HdlLetKindIO},
|
||||
options, Errors, HdlAttr,
|
||||
};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote, quote_spanned, ToTokens};
|
||||
use syn::{
|
||||
parse::{Parse, ParseStream},
|
||||
parse_quote,
|
||||
visit::{visit_pat, Visit},
|
||||
Attribute, Block, Error, FnArg, Ident, ItemFn, ItemStruct, ReturnType, Signature, Visibility,
|
||||
};
|
||||
|
||||
mod transform_body;
|
||||
|
||||
options! {
|
||||
#[options = ConfigOptions]
|
||||
#[no_ident_fragment]
|
||||
pub(crate) enum ConfigOption {
|
||||
OutlineGenerated(outline_generated),
|
||||
Extern(extern_),
|
||||
}
|
||||
}
|
||||
|
||||
options! {
|
||||
pub(crate) enum ModuleIOKind {
|
||||
Input(input),
|
||||
Output(output),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn check_name_conflicts_with_module_builder(name: &Ident) -> syn::Result<()> {
|
||||
if name == "m" {
|
||||
Err(Error::new_spanned(
|
||||
name,
|
||||
"name conflicts with implicit `m: &mut ModuleBuilder<_>`",
|
||||
))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct CheckNameConflictsWithModuleBuilderVisitor<'a> {
|
||||
pub(crate) errors: &'a mut Errors,
|
||||
}
|
||||
|
||||
impl Visit<'_> for CheckNameConflictsWithModuleBuilderVisitor<'_> {
|
||||
// TODO: change this to only check for identifiers defining new variables
|
||||
fn visit_ident(&mut self, node: &Ident) {
|
||||
self.errors
|
||||
.push_result(check_name_conflicts_with_module_builder(node));
|
||||
}
|
||||
}
|
||||
|
||||
fn retain_struct_attrs<F: FnMut(&Attribute) -> bool>(item: &mut ItemStruct, mut f: F) {
|
||||
item.attrs.retain(&mut f);
|
||||
for field in item.fields.iter_mut() {
|
||||
field.attrs.retain(&mut f);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) type ModuleIO = HdlLet<HdlLetKindIO>;
|
||||
|
||||
pub(crate) struct ModuleFn {
|
||||
attrs: Vec<Attribute>,
|
||||
config_options: HdlAttr<ConfigOptions>,
|
||||
module_kind: ModuleKind,
|
||||
vis: Visibility,
|
||||
sig: Signature,
|
||||
block: Box<Block>,
|
||||
io: Vec<ModuleIO>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
|
||||
pub(crate) enum ModuleKind {
|
||||
Extern,
|
||||
Normal,
|
||||
}
|
||||
|
||||
impl Parse for ModuleFn {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let ItemFn {
|
||||
mut attrs,
|
||||
vis,
|
||||
mut sig,
|
||||
block,
|
||||
} = input.parse()?;
|
||||
let Signature {
|
||||
ref constness,
|
||||
ref asyncness,
|
||||
ref unsafety,
|
||||
ref abi,
|
||||
fn_token: _,
|
||||
ident: _,
|
||||
ref generics,
|
||||
paren_token: _,
|
||||
ref mut inputs,
|
||||
ref variadic,
|
||||
ref output,
|
||||
} = sig;
|
||||
let mut errors = Errors::new();
|
||||
let config_options = errors
|
||||
.unwrap_or_default(HdlAttr::parse_and_take_attr(&mut attrs))
|
||||
.unwrap_or_default();
|
||||
let ConfigOptions {
|
||||
outline_generated: _,
|
||||
extern_,
|
||||
} = config_options.body;
|
||||
let module_kind = match extern_ {
|
||||
Some(_) => ModuleKind::Extern,
|
||||
None => ModuleKind::Normal,
|
||||
};
|
||||
for fn_arg in inputs {
|
||||
match fn_arg {
|
||||
FnArg::Receiver(_) => {
|
||||
errors.push(syn::Error::new_spanned(fn_arg, "self not allowed here"));
|
||||
}
|
||||
FnArg::Typed(fn_arg) => {
|
||||
visit_pat(
|
||||
&mut CheckNameConflictsWithModuleBuilderVisitor {
|
||||
errors: &mut errors,
|
||||
},
|
||||
&fn_arg.pat,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(constness) = constness {
|
||||
errors.push(syn::Error::new_spanned(constness, "const not allowed here"));
|
||||
}
|
||||
if let Some(asyncness) = asyncness {
|
||||
errors.push(syn::Error::new_spanned(asyncness, "async not allowed here"));
|
||||
}
|
||||
if let Some(unsafety) = unsafety {
|
||||
errors.push(syn::Error::new_spanned(unsafety, "unsafe not allowed here"));
|
||||
}
|
||||
if let Some(abi) = abi {
|
||||
errors.push(syn::Error::new_spanned(abi, "extern not allowed here"));
|
||||
}
|
||||
if !generics.params.is_empty() {
|
||||
errors.push(syn::Error::new_spanned(
|
||||
&generics.params,
|
||||
"generics are not supported yet",
|
||||
));
|
||||
}
|
||||
if let Some(variadic) = variadic {
|
||||
errors.push(syn::Error::new_spanned(variadic, "... not allowed here"));
|
||||
}
|
||||
if !matches!(output, ReturnType::Default) {
|
||||
errors.push(syn::Error::new_spanned(
|
||||
output,
|
||||
"return type not allowed here",
|
||||
));
|
||||
}
|
||||
let body_results = errors.ok(transform_body::transform_body(module_kind, block));
|
||||
errors.finish()?;
|
||||
let (block, io) = body_results.unwrap();
|
||||
Ok(Self {
|
||||
attrs,
|
||||
config_options,
|
||||
module_kind,
|
||||
vis,
|
||||
sig,
|
||||
block,
|
||||
io,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ModuleFn {
|
||||
pub(crate) fn generate(self) -> TokenStream {
|
||||
let Self {
|
||||
attrs,
|
||||
config_options,
|
||||
module_kind,
|
||||
vis,
|
||||
sig,
|
||||
block,
|
||||
io,
|
||||
} = self;
|
||||
let ConfigOptions {
|
||||
outline_generated: _,
|
||||
extern_: _,
|
||||
} = config_options.body;
|
||||
let mut outer_sig = sig.clone();
|
||||
let mut body_sig = sig;
|
||||
let param_names =
|
||||
Vec::from_iter(outer_sig.inputs.iter_mut().enumerate().map(|(index, arg)| {
|
||||
let FnArg::Typed(arg) = arg else {
|
||||
unreachable!("already checked");
|
||||
};
|
||||
let name = if let syn::Pat::Ident(pat) = &*arg.pat {
|
||||
pat.ident.clone()
|
||||
} else {
|
||||
format_ident!("__param{}", index)
|
||||
};
|
||||
*arg.pat = syn::Pat::Ident(syn::PatIdent {
|
||||
attrs: vec![],
|
||||
by_ref: None,
|
||||
mutability: None,
|
||||
ident: name.clone(),
|
||||
subpat: None,
|
||||
});
|
||||
name
|
||||
}));
|
||||
let module_kind_ty = match module_kind {
|
||||
ModuleKind::Extern => quote! { ::fayalite::module::ExternModule },
|
||||
ModuleKind::Normal => quote! { ::fayalite::module::NormalModule },
|
||||
};
|
||||
let fn_name = &outer_sig.ident;
|
||||
body_sig.ident = parse_quote! {__body};
|
||||
body_sig.inputs.insert(
|
||||
0,
|
||||
parse_quote! {m: &mut ::fayalite::module::ModuleBuilder<#fn_name, #module_kind_ty>},
|
||||
);
|
||||
let body_fn = ItemFn {
|
||||
attrs: vec![],
|
||||
vis: Visibility::Inherited,
|
||||
sig: body_sig,
|
||||
block,
|
||||
};
|
||||
outer_sig.output =
|
||||
parse_quote! {-> ::fayalite::intern::Interned<::fayalite::module::Module<#fn_name>>};
|
||||
let io_flips = io
|
||||
.iter()
|
||||
.map(|io| match io.kind.kind {
|
||||
ModuleIOKind::Input((input,)) => quote_spanned! {input.span=>
|
||||
#[hdl(flip)]
|
||||
},
|
||||
ModuleIOKind::Output(_) => quote! {},
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let io_types = io.iter().map(|io| &io.kind.ty).collect::<Vec<_>>();
|
||||
let io_names = io.iter().map(|io| &io.name).collect::<Vec<_>>();
|
||||
let fn_name_str = fn_name.to_string();
|
||||
let block = parse_quote! {{
|
||||
#body_fn
|
||||
::fayalite::module::ModuleBuilder::run(#fn_name_str, |m| __body(m, #(#param_names,)*))
|
||||
}};
|
||||
let fixed_type = io.iter().all(|io| io.kind.ty_expr.is_none());
|
||||
let struct_options = if fixed_type {
|
||||
quote! { #[hdl(fixed_type)] }
|
||||
} else {
|
||||
quote! {}
|
||||
};
|
||||
let the_struct: ItemStruct = parse_quote! {
|
||||
#[derive(::fayalite::__std::clone::Clone,
|
||||
::fayalite::__std::hash::Hash,
|
||||
::fayalite::__std::cmp::PartialEq,
|
||||
::fayalite::__std::cmp::Eq,
|
||||
::fayalite::__std::fmt::Debug)]
|
||||
#[allow(non_camel_case_types)]
|
||||
#struct_options
|
||||
#vis struct #fn_name {
|
||||
#(
|
||||
#io_flips
|
||||
#vis #io_names: #io_types,)*
|
||||
}
|
||||
};
|
||||
let mut struct_without_hdl_attrs = the_struct.clone();
|
||||
let mut struct_without_derives = the_struct;
|
||||
retain_struct_attrs(&mut struct_without_hdl_attrs, |attr| !is_hdl_attr(attr));
|
||||
retain_struct_attrs(&mut struct_without_derives, |attr| {
|
||||
!attr.path().is_ident("derive")
|
||||
});
|
||||
let outer_fn = ItemFn {
|
||||
attrs,
|
||||
vis,
|
||||
sig: outer_sig,
|
||||
block,
|
||||
};
|
||||
let mut retval = outer_fn.into_token_stream();
|
||||
struct_without_hdl_attrs.to_tokens(&mut retval);
|
||||
retval.extend(
|
||||
crate::value_derive_struct::value_derive_struct(struct_without_derives).unwrap(),
|
||||
);
|
||||
retval
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue