forked from libre-chip/fayalite
283 lines
8.8 KiB
Rust
283 lines
8.8 KiB
Rust
|
// 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
|
||
|
}
|
||
|
}
|