fayalite/crates/fayalite-proc-macros-impl/src/module/transform_body.rs

1577 lines
47 KiB
Rust
Raw Normal View History

2024-06-11 06:09:13 +00:00
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
fold::{impl_fold, DoFold},
is_hdl_attr, kw,
module::{check_name_conflicts_with_module_builder, ModuleIO, ModuleIOKind, ModuleKind},
options, Errors, HdlAttr,
};
use num_bigint::{BigInt, Sign};
use proc_macro2::{Span, TokenStream};
use quote::{quote, quote_spanned, ToTokens};
use std::{borrow::Borrow, convert::Infallible};
use syn::{
fold::{fold_expr, fold_expr_lit, fold_expr_unary, fold_local, fold_stmt, Fold},
parenthesized,
parse::{Nothing, Parse, ParseStream},
parse_quote, parse_quote_spanned,
spanned::Spanned,
token::Paren,
Attribute, Block, Error, Expr, ExprIf, ExprLet, ExprLit, ExprRepeat, ExprUnary,
GenericArgument, Ident, Item, Lit, LitStr, Local, LocalInit, Pat, Token, Type, UnOp,
};
mod expand_aggregate_literals;
mod expand_match;
options! {
pub(crate) enum LetFnKind {
Input(input),
Output(output),
Instance(instance),
RegBuilder(reg_builder),
Wire(wire),
Memory(memory),
MemoryArray(memory_array),
MemoryWithInit(memory_with_init),
}
}
macro_rules! with_debug_clone_and_fold {
(
$(#[$meta:meta])*
$struct_vis:vis struct $name:ident<$($T:ident $(= $default_ty:ty)?),*>
$(where {$($where:tt)*})?
{
$($field_vis:vis $field:ident: $field_ty:ty,)*
}
) => {
$(#[$meta])*
$struct_vis struct $name<$($T $(= $default_ty)?),*>
$(where $($where)*)?
{
$($field_vis $field: $field_ty,)*
}
crate::fold::impl_fold! {
struct $name<$($T,)*>
$(where ($($where)*))?
{
$($field: $field_ty,)*
}
}
// workaround for #[derive()] not working with generic structs with type macros
impl<$($T: std::fmt::Debug),*> std::fmt::Debug for $name<$($T),*>
$(where $($where)*)?
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct(stringify!($name))
$(.field(stringify!($field), &self.$field))*
.finish()
}
}
impl<$($T: Clone),*> Clone for $name<$($T),*>
$(where $($where)*)?
{
fn clone(&self) -> Self {
Self {
$($field: self.$field.clone(),)*
}
}
}
};
}
pub(crate) use with_debug_clone_and_fold;
with_debug_clone_and_fold! {
pub(crate) struct HdlLetKindIO<Kind = ModuleIOKind> {
pub(crate) colon_token: Token![:],
pub(crate) ty: Box<Type>,
pub(crate) m: kw::m,
pub(crate) dot_token: Token![.],
pub(crate) kind: Kind,
pub(crate) paren: Paren,
pub(crate) ty_expr: Option<Box<Expr>>,
}
}
pub(crate) fn parse_single_fn_arg(input: ParseStream) -> syn::Result<Box<Expr>> {
let retval = input.parse()?;
let _: Option<Token![,]> = input.parse()?;
Ok(retval)
}
pub(crate) fn parse_optional_fn_arg(input: ParseStream) -> syn::Result<Option<Box<Expr>>> {
if input.is_empty() {
return Ok(None);
}
parse_single_fn_arg(input).map(Some)
}
impl<Kind: ToTokens> HdlLetKindToTokens for HdlLetKindIO<Kind> {
fn ty_to_tokens(&self, tokens: &mut TokenStream) {
let Self {
colon_token,
ty,
m: _,
dot_token: _,
kind: _,
paren: _,
ty_expr: _,
} = self;
colon_token.to_tokens(tokens);
ty.to_tokens(tokens);
}
fn expr_to_tokens(&self, tokens: &mut TokenStream) {
let Self {
colon_token: _,
ty: _,
m,
dot_token,
kind,
paren,
ty_expr,
} = self;
m.to_tokens(tokens);
dot_token.to_tokens(tokens);
kind.to_tokens(tokens);
paren.surround(tokens, |tokens| ty_expr.to_tokens(tokens));
}
}
#[derive(Clone, Debug)]
pub(crate) struct HdlLetKindInstance {
pub(crate) m: kw::m,
pub(crate) dot_token: Token![.],
pub(crate) instance: kw::instance,
pub(crate) paren: Paren,
pub(crate) module: Box<Expr>,
}
impl_fold! {
struct HdlLetKindInstance<> {
m: kw::m,
dot_token: Token![.],
instance: kw::instance,
paren: Paren,
module: Box<Expr>,
}
}
impl HdlLetKindToTokens for HdlLetKindInstance {
fn ty_to_tokens(&self, _tokens: &mut TokenStream) {}
fn expr_to_tokens(&self, tokens: &mut TokenStream) {
let Self {
m,
dot_token,
instance,
paren,
module,
} = self;
m.to_tokens(tokens);
dot_token.to_tokens(tokens);
instance.to_tokens(tokens);
paren.surround(tokens, |tokens| module.to_tokens(tokens));
}
}
#[derive(Clone, Debug)]
pub(crate) struct RegBuilderClockDomain {
pub(crate) dot_token: Token![.],
pub(crate) clock_domain: kw::clock_domain,
pub(crate) paren: Paren,
pub(crate) expr: Box<Expr>,
}
impl_fold! {
struct RegBuilderClockDomain<> {
dot_token: Token![.],
clock_domain: kw::clock_domain,
paren: Paren,
expr: Box<Expr>,
}
}
impl Parse for RegBuilderClockDomain {
fn parse(input: ParseStream) -> syn::Result<Self> {
let in_parens;
Ok(Self {
dot_token: input.parse()?,
clock_domain: input.parse()?,
paren: parenthesized!(in_parens in input),
expr: in_parens.call(parse_single_fn_arg)?,
})
}
}
impl ToTokens for RegBuilderClockDomain {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self {
dot_token,
clock_domain,
paren,
expr,
} = self;
dot_token.to_tokens(tokens);
clock_domain.to_tokens(tokens);
paren.surround(tokens, |tokens| expr.to_tokens(tokens));
}
}
#[derive(Clone, Debug)]
pub(crate) enum RegBuilderReset {
NoReset {
dot_token: Token![.],
no_reset: kw::no_reset,
paren: Paren,
ty_expr: Option<Box<Expr>>,
},
Reset {
dot_token: Token![.],
reset: kw::reset,
paren: Paren,
init_expr: Box<Expr>,
},
ResetDefault {
dot_token: Token![.],
reset_default: kw::reset_default,
paren: Paren,
},
}
impl_fold! {
enum RegBuilderReset<> {
NoReset {
dot_token: Token![.],
no_reset: kw::no_reset,
paren: Paren,
ty_expr: Option<Box<Expr>>,
},
Reset {
dot_token: Token![.],
reset: kw::reset,
paren: Paren,
init_expr: Box<Expr>,
},
ResetDefault {
dot_token: Token![.],
reset_default: kw::reset_default,
paren: Paren,
},
}
}
impl Parse for RegBuilderReset {
fn parse(input: ParseStream) -> syn::Result<Self> {
let dot_token = input.parse()?;
let paren_contents;
match RegBuilderMethod::parse(input, false, true)? {
RegBuilderMethod::ClockDomain(_) => unreachable!(),
RegBuilderMethod::NoReset(no_reset) => Ok(Self::NoReset {
dot_token,
no_reset,
paren: parenthesized!(paren_contents in input),
ty_expr: paren_contents.call(parse_optional_fn_arg)?,
}),
RegBuilderMethod::Reset(reset) => Ok(Self::Reset {
dot_token,
reset,
paren: parenthesized!(paren_contents in input),
init_expr: paren_contents.call(parse_single_fn_arg)?,
}),
RegBuilderMethod::ResetDefault(reset_default) => Ok(Self::ResetDefault {
dot_token,
reset_default,
paren: parenthesized!(paren_contents in input),
}),
}
}
}
impl ToTokens for RegBuilderReset {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
RegBuilderReset::NoReset {
dot_token,
no_reset,
paren,
ty_expr,
} => {
dot_token.to_tokens(tokens);
no_reset.to_tokens(tokens);
paren.surround(tokens, |tokens| ty_expr.to_tokens(tokens));
}
RegBuilderReset::Reset {
dot_token,
reset,
paren,
init_expr,
} => {
dot_token.to_tokens(tokens);
reset.to_tokens(tokens);
paren.surround(tokens, |tokens| init_expr.to_tokens(tokens));
}
RegBuilderReset::ResetDefault {
dot_token,
reset_default,
paren,
} => {
dot_token.to_tokens(tokens);
reset_default.to_tokens(tokens);
paren.surround(tokens, |_| {});
}
}
}
}
macro_rules! make_builder_method_enum {
(
#[parse_args($($parse_arg:ident: $parse_arg_ty:ty),+)]
$vis:vis enum $enum_name:ident {
$(#[cond = $cond:expr]
$variant:ident($kw:ident),)+
}
) => {
#[derive(Clone, Debug)]
$vis enum $enum_name {
$(
#[allow(dead_code)]
$variant(kw::$kw),
)+
}
impl $enum_name {
$vis fn parse(input: ParseStream, $($parse_arg: $parse_arg_ty),+) -> syn::Result<Self> {
let lookahead = input.lookahead1();
$(if $cond && lookahead.peek(kw::$kw) {
return input.parse().map(Self::$variant)
})+
Err(lookahead.error())
}
$vis fn parse_dot_prefixed(
input: ParseStream,
$($parse_arg: $parse_arg_ty,)+
) -> syn::Result<(Token![.], Self)> {
2024-06-11 06:09:13 +00:00
let dot = input.parse()?;
Ok((dot, Self::parse(input, $($parse_arg),+)?))
}
}
};
}
make_builder_method_enum! {
#[parse_args(need_clock_domain: bool, need_reset: bool)]
pub(crate) enum RegBuilderMethod {
#[cond = need_clock_domain]
ClockDomain(clock_domain),
#[cond = need_reset]
NoReset(no_reset),
#[cond = need_reset]
Reset(reset),
#[cond = need_reset]
ResetDefault(reset_default),
}
}
#[derive(Clone, Debug)]
pub(crate) struct HdlLetKindRegBuilder {
pub(crate) ty: Option<(Token![:], Box<Type>)>,
pub(crate) m: kw::m,
pub(crate) dot_token: Token![.],
pub(crate) reg_builder: kw::reg_builder,
pub(crate) reg_builder_paren: Paren,
pub(crate) clock_domain: Option<RegBuilderClockDomain>,
pub(crate) reset: RegBuilderReset,
}
impl_fold! {
struct HdlLetKindRegBuilder<> {
ty: Option<(Token![:], Box<Type>)>,
m: kw::m,
dot_token: Token![.],
reg_builder: kw::reg_builder,
reg_builder_paren: Paren,
clock_domain: Option<RegBuilderClockDomain>,
reset: RegBuilderReset,
}
}
impl HdlLetKindRegBuilder {
fn rest_of_parse(
input: ParseStream,
parsed_ty: Option<(Token![:], Box<Type>)>,
_after_ty: Token![=],
m: kw::m,
dot_token: Token![.],
reg_builder: kw::reg_builder,
) -> syn::Result<Self> {
let _reg_builder_paren_inner;
let reg_builder_paren = parenthesized!(_reg_builder_paren_inner in input);
let mut clock_domain = None;
match RegBuilderMethod::parse_dot_prefixed(&input.fork(), true, true)?.1 {
RegBuilderMethod::ClockDomain(_) => clock_domain = Some(input.parse()?),
RegBuilderMethod::NoReset(_)
| RegBuilderMethod::Reset(_)
| RegBuilderMethod::ResetDefault(_) => {}
}
let reset = input.parse()?;
if clock_domain.is_none() {
match RegBuilderMethod::parse_dot_prefixed(&input.fork(), true, false)?.1 {
RegBuilderMethod::ClockDomain(_) => clock_domain = Some(input.parse()?),
RegBuilderMethod::NoReset(_)
| RegBuilderMethod::Reset(_)
| RegBuilderMethod::ResetDefault(_) => unreachable!(),
}
}
Ok(Self {
ty: parsed_ty,
m,
dot_token,
reg_builder,
reg_builder_paren,
clock_domain,
reset,
})
}
}
impl HdlLetKindToTokens for HdlLetKindRegBuilder {
fn ty_to_tokens(&self, tokens: &mut TokenStream) {
if let Some((colon_token, ty)) = &self.ty {
colon_token.to_tokens(tokens);
ty.to_tokens(tokens);
}
}
fn expr_to_tokens(&self, tokens: &mut TokenStream) {
let Self {
ty: _,
m,
dot_token,
reg_builder,
reg_builder_paren,
clock_domain,
reset,
} = self;
m.to_tokens(tokens);
dot_token.to_tokens(tokens);
reg_builder.to_tokens(tokens);
reg_builder_paren.surround(tokens, |_tokens| {});
clock_domain.to_tokens(tokens);
reset.to_tokens(tokens);
}
}
#[derive(Clone, Debug)]
pub(crate) struct HdlLetKindWire {
pub(crate) ty: Option<(Token![:], Box<Type>)>,
pub(crate) m: kw::m,
pub(crate) dot_token: Token![.],
pub(crate) wire: kw::wire,
pub(crate) paren: Paren,
pub(crate) ty_expr: Option<Box<Expr>>,
}
impl_fold! {
struct HdlLetKindWire<> {
ty: Option<(Token![:], Box<Type>)>,
m: kw::m,
dot_token: Token![.],
wire: kw::wire,
paren: Paren,
ty_expr: Option<Box<Expr>>,
}
}
impl HdlLetKindToTokens for HdlLetKindWire {
fn ty_to_tokens(&self, tokens: &mut TokenStream) {
if let Some((colon_token, ty)) = &self.ty {
colon_token.to_tokens(tokens);
ty.to_tokens(tokens);
}
}
fn expr_to_tokens(&self, tokens: &mut TokenStream) {
let Self {
ty: _,
m,
dot_token,
wire,
paren,
ty_expr,
} = self;
m.to_tokens(tokens);
dot_token.to_tokens(tokens);
wire.to_tokens(tokens);
paren.surround(tokens, |tokens| ty_expr.to_tokens(tokens));
}
}
options! {
pub(crate) enum MemoryFnName {
Memory(memory),
MemoryArray(memory_array),
MemoryWithInit(memory_with_init),
}
}
#[derive(Clone, Debug)]
pub(crate) enum MemoryFn {
Memory {
memory: kw::memory,
paren: Paren,
ty_expr: Option<Box<Expr>>,
},
MemoryArray {
memory_array: kw::memory_array,
paren: Paren,
ty_expr: Option<Box<Expr>>,
},
MemoryWithInit {
memory_with_init: kw::memory_with_init,
paren: Paren,
init_expr: Box<Expr>,
},
}
impl_fold! {
enum MemoryFn<> {
Memory {
memory: kw::memory,
paren: Paren,
ty_expr: Option<Box<Expr>>,
},
MemoryArray {
memory_array: kw::memory_array,
paren: Paren,
ty_expr: Option<Box<Expr>>,
},
MemoryWithInit {
memory_with_init: kw::memory_with_init,
paren: Paren,
init_expr: Box<Expr>,
},
}
}
impl MemoryFn {
fn name(&self) -> MemoryFnName {
match *self {
MemoryFn::Memory { memory, .. } => MemoryFnName::Memory((memory,)),
MemoryFn::MemoryArray { memory_array, .. } => {
MemoryFnName::MemoryArray((memory_array,))
}
MemoryFn::MemoryWithInit {
memory_with_init, ..
} => MemoryFnName::MemoryWithInit((memory_with_init,)),
}
}
fn parse_rest(input: ParseStream, memory_fn_name: MemoryFnName) -> syn::Result<Self> {
let paren_contents;
match memory_fn_name {
MemoryFnName::Memory((memory,)) => Ok(Self::Memory {
memory,
paren: parenthesized!(paren_contents in input),
ty_expr: paren_contents.call(parse_optional_fn_arg)?,
}),
MemoryFnName::MemoryArray((memory_array,)) => Ok(Self::MemoryArray {
memory_array,
paren: parenthesized!(paren_contents in input),
ty_expr: paren_contents.call(parse_optional_fn_arg)?,
}),
MemoryFnName::MemoryWithInit((memory_with_init,)) => Ok(Self::MemoryWithInit {
memory_with_init,
paren: parenthesized!(paren_contents in input),
init_expr: paren_contents.call(parse_single_fn_arg)?,
}),
}
}
}
impl ToTokens for MemoryFn {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
MemoryFn::Memory {
memory,
paren,
ty_expr,
} => {
memory.to_tokens(tokens);
paren.surround(tokens, |tokens| ty_expr.to_tokens(tokens));
}
MemoryFn::MemoryArray {
memory_array,
paren,
ty_expr,
} => {
memory_array.to_tokens(tokens);
paren.surround(tokens, |tokens| ty_expr.to_tokens(tokens));
}
MemoryFn::MemoryWithInit {
memory_with_init,
paren,
init_expr,
} => {
memory_with_init.to_tokens(tokens);
paren.surround(tokens, |tokens| init_expr.to_tokens(tokens));
}
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct HdlLetKindMemory {
pub(crate) ty: Option<(Token![:], Box<Type>)>,
pub(crate) m: kw::m,
pub(crate) dot_token: Token![.],
pub(crate) memory_fn: MemoryFn,
}
impl_fold! {
struct HdlLetKindMemory<> {
ty: Option<(Token![:], Box<Type>)>,
m: kw::m,
dot_token: Token![.],
memory_fn: MemoryFn,
}
}
impl HdlLetKindToTokens for HdlLetKindMemory {
fn ty_to_tokens(&self, tokens: &mut TokenStream) {
if let Some((colon_token, ty)) = &self.ty {
colon_token.to_tokens(tokens);
ty.to_tokens(tokens);
}
}
fn expr_to_tokens(&self, tokens: &mut TokenStream) {
let Self {
ty: _,
m,
dot_token,
memory_fn,
} = self;
m.to_tokens(tokens);
dot_token.to_tokens(tokens);
memory_fn.to_tokens(tokens);
}
}
impl HdlLetKindMemory {
fn rest_of_parse(
input: ParseStream,
parsed_ty: Option<(Token![:], Box<Type>)>,
_after_ty: Token![=],
m: kw::m,
dot_token: Token![.],
memory_fn_name: MemoryFnName,
) -> syn::Result<Self> {
Ok(Self {
ty: parsed_ty,
m,
dot_token,
memory_fn: MemoryFn::parse_rest(input, memory_fn_name)?,
})
}
}
#[derive(Clone, Debug)]
pub(crate) enum HdlLetKind {
IO(HdlLetKindIO),
Instance(HdlLetKindInstance),
RegBuilder(HdlLetKindRegBuilder),
Wire(HdlLetKindWire),
Memory(HdlLetKindMemory),
}
impl_fold! {
enum HdlLetKind<> {
IO(HdlLetKindIO),
Instance(HdlLetKindInstance),
RegBuilder(HdlLetKindRegBuilder),
Wire(HdlLetKindWire),
Memory(HdlLetKindMemory),
}
}
fn parsed_ty_or_err(
parsed_ty: Option<(Token![:], Box<Type>)>,
after_ty: Token![=],
) -> syn::Result<(Token![:], Box<Type>)> {
if let Some(retval) = parsed_ty {
Ok(retval)
} else {
Err(Error::new_spanned(after_ty, "missing `:`"))
}
}
impl HdlLetKindIO {
fn rest_of_parse(
input: ParseStream,
parsed_ty: Option<(Token![:], Box<Type>)>,
after_ty: Token![=],
m: kw::m,
dot_token: Token![.],
kind: ModuleIOKind,
) -> syn::Result<Self> {
let (colon_token, ty) = parsed_ty_or_err(parsed_ty, after_ty)?;
let paren_contents;
Ok(Self {
colon_token,
ty,
m,
dot_token,
kind,
paren: parenthesized!(paren_contents in input),
ty_expr: paren_contents.call(parse_optional_fn_arg)?,
})
}
}
impl HdlLetKindParse for HdlLetKind {
type ParsedTy = Option<(Token![:], Box<Type>)>;
fn parse_ty(input: ParseStream) -> syn::Result<Self::ParsedTy> {
let ty_lookahead = input.lookahead1();
if ty_lookahead.peek(Token![:]) {
Ok(Some((input.parse()?, input.parse()?)))
} else if ty_lookahead.peek(Token![=]) {
Ok(None)
} else {
Err(ty_lookahead.error())
}
}
fn parse_expr(
_name: &Ident,
parsed_ty: Self::ParsedTy,
after_ty: Token![=],
input: ParseStream,
) -> syn::Result<Self> {
let m = input.parse()?;
let dot_token = input.parse()?;
let kind: LetFnKind = input.parse()?;
match kind {
LetFnKind::Input(input_token) => HdlLetKindIO::rest_of_parse(
input,
parsed_ty,
after_ty,
m,
dot_token,
ModuleIOKind::Input(input_token),
)
.map(Self::IO),
LetFnKind::Output(output) => HdlLetKindIO::rest_of_parse(
input,
parsed_ty,
after_ty,
m,
dot_token,
ModuleIOKind::Output(output),
)
.map(Self::IO),
LetFnKind::Instance((instance,)) => {
if let Some(parsed_ty) = parsed_ty {
return Err(Error::new_spanned(
parsed_ty.1,
"type annotation not allowed for instance",
));
}
let paren_contents;
Ok(Self::Instance(HdlLetKindInstance {
m,
dot_token,
instance,
paren: parenthesized!(paren_contents in input),
module: paren_contents.call(parse_single_fn_arg)?,
}))
}
LetFnKind::RegBuilder((reg_builder,)) => HdlLetKindRegBuilder::rest_of_parse(
input,
parsed_ty,
after_ty,
m,
dot_token,
reg_builder,
)
.map(Self::RegBuilder),
LetFnKind::Wire((wire,)) => {
let paren_contents;
Ok(Self::Wire(HdlLetKindWire {
ty: parsed_ty,
m,
dot_token,
wire,
paren: parenthesized!(paren_contents in input),
ty_expr: paren_contents.call(parse_optional_fn_arg)?,
}))
}
LetFnKind::Memory(fn_name) => HdlLetKindMemory::rest_of_parse(
input,
parsed_ty,
after_ty,
m,
dot_token,
MemoryFnName::Memory(fn_name),
)
.map(Self::Memory),
LetFnKind::MemoryArray(fn_name) => HdlLetKindMemory::rest_of_parse(
input,
parsed_ty,
after_ty,
m,
dot_token,
MemoryFnName::MemoryArray(fn_name),
)
.map(Self::Memory),
LetFnKind::MemoryWithInit(fn_name) => HdlLetKindMemory::rest_of_parse(
input,
parsed_ty,
after_ty,
m,
dot_token,
MemoryFnName::MemoryWithInit(fn_name),
)
.map(Self::Memory),
}
}
}
impl HdlLetKindToTokens for HdlLetKind {
fn ty_to_tokens(&self, tokens: &mut TokenStream) {
match self {
HdlLetKind::IO(v) => v.ty_to_tokens(tokens),
HdlLetKind::Instance(v) => v.ty_to_tokens(tokens),
HdlLetKind::RegBuilder(v) => v.ty_to_tokens(tokens),
HdlLetKind::Wire(v) => v.ty_to_tokens(tokens),
HdlLetKind::Memory(v) => v.ty_to_tokens(tokens),
}
}
fn expr_to_tokens(&self, tokens: &mut TokenStream) {
match self {
HdlLetKind::IO(v) => v.expr_to_tokens(tokens),
HdlLetKind::Instance(v) => v.expr_to_tokens(tokens),
HdlLetKind::RegBuilder(v) => v.expr_to_tokens(tokens),
HdlLetKind::Wire(v) => v.expr_to_tokens(tokens),
HdlLetKind::Memory(v) => v.expr_to_tokens(tokens),
}
}
}
with_debug_clone_and_fold! {
#[allow(dead_code)]
pub(crate) struct HdlLet<Kind = HdlLetKind> {
pub(crate) attrs: Vec<Attribute>,
pub(crate) hdl_attr: HdlAttr<Nothing>,
pub(crate) let_token: Token![let],
pub(crate) mut_token: Option<Token![mut]>,
pub(crate) name: Ident,
pub(crate) eq_token: Token![=],
pub(crate) kind: Kind,
pub(crate) semi_token: Token![;],
}
}
impl<Kind> HdlLet<Kind> {
pub(crate) fn try_map<Kind2, E>(
self,
f: impl FnOnce(Kind) -> Result<Kind2, E>,
) -> Result<HdlLet<Kind2>, E> {
let Self {
attrs,
hdl_attr,
let_token,
mut_token,
name,
eq_token,
kind,
semi_token,
} = self;
let kind = f(kind)?;
Ok(HdlLet {
attrs,
hdl_attr,
let_token,
mut_token,
name,
eq_token,
kind,
semi_token,
})
}
pub(crate) fn map<Kind2>(self, f: impl FnOnce(Kind) -> Kind2) -> HdlLet<Kind2> {
match self.try_map(|kind| Ok::<Kind2, Infallible>(f(kind))) {
Ok(v) => v,
Err(e) => match e {},
}
}
}
pub trait HdlLetKindParse: Sized {
type ParsedTy;
fn parse_ty(input: ParseStream) -> syn::Result<Self::ParsedTy>;
fn parse_expr(
name: &Ident,
parsed_ty: Self::ParsedTy,
after_ty: Token![=],
input: ParseStream,
) -> syn::Result<Self>;
}
pub trait HdlLetKindToTokens {
fn ty_to_tokens(&self, tokens: &mut TokenStream);
fn expr_to_tokens(&self, tokens: &mut TokenStream);
}
impl<Kind: HdlLetKindParse> Parse for HdlLet<Kind> {
fn parse(input: ParseStream) -> syn::Result<Self> {
let mut attrs = Attribute::parse_outer(input)?;
let hdl_attr = HdlAttr::parse_and_take_attr(&mut attrs)?;
let let_token = input.parse()?;
let mut_token = input.parse()?;
let hdl_attr = hdl_attr.ok_or_else(|| Error::new_spanned(let_token, "missing #[hdl]"))?;
let name = input.parse()?;
check_name_conflicts_with_module_builder(&name)?;
let parsed_ty = Kind::parse_ty(input)?;
let eq_token = input.parse()?;
let kind = Kind::parse_expr(&name, parsed_ty, eq_token, input)?;
let retval = Self {
attrs,
hdl_attr,
let_token,
mut_token,
name,
eq_token,
kind,
semi_token: input.parse()?,
};
Ok(retval)
}
}
impl<Kind: HdlLetKindToTokens> ToTokens for HdlLet<Kind> {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self {
attrs,
hdl_attr,
let_token,
mut_token,
name,
eq_token,
kind,
semi_token,
} = self;
for attr in attrs {
attr.to_tokens(tokens);
}
hdl_attr.to_tokens(tokens);
let_token.to_tokens(tokens);
mut_token.to_tokens(tokens);
name.to_tokens(tokens);
kind.ty_to_tokens(tokens);
eq_token.to_tokens(tokens);
kind.expr_to_tokens(tokens);
semi_token.to_tokens(tokens);
}
}
fn parse_quote_let_pat<T, R: ToTokens, C: Borrow<Token![:]>>(
mut_token: &Option<Token![mut]>,
name: &Ident,
ty: Option<(C, T)>,
map_ty: impl FnOnce(T) -> R,
) -> Pat {
match ty {
Some((colon_token, ty)) => {
let colon_token = colon_token.borrow();
let ty = map_ty(ty);
Pat::Type(parse_quote! { #mut_token #name #colon_token #ty })
}
None => parse_quote! { #mut_token #name },
}
}
fn wrap_ty_with_expr(ty: impl ToTokens) -> Type {
parse_quote_spanned! {ty.span()=>
::fayalite::expr::Expr<#ty>
}
}
fn unwrap_or_static_type(expr: Option<impl ToTokens>, span: Span) -> TokenStream {
2024-06-11 06:09:13 +00:00
expr.map(ToTokens::into_token_stream).unwrap_or_else(|| {
quote_spanned! {span=>
::fayalite::ty::StaticType::static_type()
2024-06-11 06:09:13 +00:00
}
})
}
struct ImplicitName<T: ToString> {
name: T,
span: Span,
}
impl<T: ToString> ToTokens for ImplicitName<T> {
fn to_tokens(&self, tokens: &mut TokenStream) {
let name = LitStr::new(&self.name.to_string(), self.span);
quote_spanned! {self.span=>
::fayalite::module::ImplicitName(#name)
}
.to_tokens(tokens);
}
}
struct Visitor {
module_kind: ModuleKind,
errors: Errors,
io: Vec<ModuleIO>,
block_depth: usize,
}
impl Visitor {
fn take_hdl_attr<T: Parse>(&mut self, attrs: &mut Vec<Attribute>) -> Option<HdlAttr<T>> {
self.errors.unwrap_or(
HdlAttr::parse_and_take_attr(attrs),
Some(syn::parse2::<T>(quote! {}).unwrap().into()),
)
}
fn require_normal_module(&mut self, spanned: impl ToTokens) {
match self.module_kind {
ModuleKind::Extern => {
self.errors
.error(spanned, "not allowed in #[hdl_module(extern)]");
}
ModuleKind::Normal => {}
}
}
fn process_hdl_if(&mut self, hdl_attr: HdlAttr<Nothing>, expr_if: ExprIf) -> Expr {
let ExprIf {
attrs,
if_token,
cond,
then_branch,
else_branch,
} = expr_if;
self.require_normal_module(if_token);
let else_expr = else_branch.unzip().1.map(|else_expr| match *else_expr {
Expr::If(expr_if) => self.process_hdl_if(hdl_attr.clone(), expr_if),
expr => expr,
});
if let Expr::Let(ExprLet {
attrs: let_attrs,
let_token: _,
pat,
eq_token: _,
expr,
}) = *cond
{
let else_expr = else_expr.unwrap_or_else(|| parse_quote_spanned! {if_token.span=> {}});
return self.process_hdl_match(
hdl_attr,
parse_quote_spanned! {if_token.span=>
#(#attrs)*
match #expr {
#(#let_attrs)* #pat => #then_branch,
_ => #else_expr,
}
},
);
}
if let Some(else_expr) = else_expr {
parse_quote_spanned! {if_token.span=>
#(#attrs)*
{
let mut __scope = m.if_(#cond);
let _: () = #then_branch;
let mut __scope = __scope.else_();
let _: () = #else_expr;
}
}
} else {
parse_quote_spanned! {if_token.span=>
#(#attrs)*
{
let mut __scope = m.if_(#cond);
let _: () = #then_branch;
}
}
}
}
fn process_hdl_let_io(&mut self, hdl_let: HdlLet<HdlLetKindIO>) -> Local {
let name = &hdl_let.name;
let colon_token = hdl_let.kind.colon_token;
let ty = &hdl_let.kind.ty;
let m = hdl_let.kind.m;
let dot = hdl_let.kind.dot_token;
let kind = hdl_let.kind.kind;
let ty_expr = unwrap_or_static_type(hdl_let.kind.ty_expr.as_ref(), kind.span());
2024-06-11 06:09:13 +00:00
let mut expr = quote! {#m #dot #kind};
hdl_let.kind.paren.surround(&mut expr, |expr| {
let name_str = ImplicitName {
name,
span: name.span(),
};
quote_spanned! {name.span()=>
#name_str, #ty_expr
}
.to_tokens(expr);
});
let mut attrs = hdl_let.attrs.clone();
match self.module_kind {
ModuleKind::Extern => attrs.push(parse_quote_spanned! {hdl_let.let_token.span=>
#[allow(unused_variables)]
}),
ModuleKind::Normal => {}
}
let let_stmt = Local {
attrs,
let_token: hdl_let.let_token,
pat: parse_quote_let_pat(
&hdl_let.mut_token,
name,
Some((colon_token, &ty)),
wrap_ty_with_expr,
),
init: Some(LocalInit {
eq_token: hdl_let.eq_token,
expr: parse_quote! { #expr },
diverge: None,
}),
semi_token: hdl_let.semi_token,
};
self.io.push(hdl_let);
let_stmt
}
fn process_hdl_let_instance(&mut self, hdl_let: HdlLet<HdlLetKindInstance>) -> Local {
let HdlLet {
attrs,
hdl_attr: _,
let_token,
mut_token,
name,
eq_token,
kind:
HdlLetKindInstance {
m,
dot_token,
instance,
paren,
module,
},
semi_token,
} = hdl_let;
self.require_normal_module(instance);
let mut expr = quote! {#m #dot_token #instance};
paren.surround(&mut expr, |expr| {
let name_str = ImplicitName {
name: &name,
span: name.span(),
};
quote_spanned! {name.span()=>
#name_str, #module
}
.to_tokens(expr);
});
Local {
attrs,
let_token,
pat: parse_quote! { #mut_token #name },
init: Some(LocalInit {
eq_token,
expr: parse_quote! { #expr },
diverge: None,
}),
semi_token,
}
}
fn process_hdl_let_reg_builder(&mut self, hdl_let: HdlLet<HdlLetKindRegBuilder>) -> Local {
let name = &hdl_let.name;
let m = hdl_let.kind.m;
let dot = hdl_let.kind.dot_token;
let reg_builder = hdl_let.kind.reg_builder;
self.require_normal_module(reg_builder);
let mut expr = quote! {#m #dot #reg_builder};
hdl_let.kind.reg_builder_paren.surround(&mut expr, |expr| {
let name_str = ImplicitName {
name,
span: name.span(),
};
quote_spanned! {name.span()=>
#name_str
}
.to_tokens(expr);
});
hdl_let.kind.clock_domain.to_tokens(&mut expr);
match hdl_let.kind.reset {
RegBuilderReset::NoReset {
dot_token,
no_reset,
paren,
ty_expr,
} => {
let ty_expr = unwrap_or_static_type(ty_expr.as_ref(), reg_builder.span());
2024-06-11 06:09:13 +00:00
dot_token.to_tokens(&mut expr);
no_reset.to_tokens(&mut expr);
paren.surround(&mut expr, |expr| ty_expr.to_tokens(expr));
}
RegBuilderReset::Reset { .. } | RegBuilderReset::ResetDefault { .. } => {
hdl_let.kind.reset.to_tokens(&mut expr);
}
}
Local {
attrs: hdl_let.attrs.clone(),
let_token: hdl_let.let_token,
pat: parse_quote_let_pat(
&hdl_let.mut_token,
name,
hdl_let.kind.ty.clone(),
wrap_ty_with_expr,
),
init: Some(LocalInit {
eq_token: hdl_let.eq_token,
expr: parse_quote_spanned! {reg_builder.span()=>
#expr.build()
},
diverge: None,
}),
semi_token: hdl_let.semi_token,
}
}
fn process_hdl_let_wire(&mut self, hdl_let: HdlLet<HdlLetKindWire>) -> Local {
let name = &hdl_let.name;
let m = hdl_let.kind.m;
let dot = hdl_let.kind.dot_token;
let wire = hdl_let.kind.wire;
self.require_normal_module(wire);
let ty_expr = unwrap_or_static_type(hdl_let.kind.ty_expr.as_ref(), wire.span());
2024-06-11 06:09:13 +00:00
let mut expr = quote! {#m #dot #wire};
hdl_let.kind.paren.surround(&mut expr, |expr| {
let name_str = ImplicitName {
name,
span: name.span(),
};
quote_spanned! {name.span()=>
#name_str, #ty_expr
}
.to_tokens(expr);
});
Local {
attrs: hdl_let.attrs.clone(),
let_token: hdl_let.let_token,
pat: parse_quote_let_pat(
&hdl_let.mut_token,
name,
hdl_let.kind.ty.clone(),
wrap_ty_with_expr,
),
init: Some(LocalInit {
eq_token: hdl_let.eq_token,
expr: parse_quote! { #expr },
diverge: None,
}),
semi_token: hdl_let.semi_token,
}
}
fn process_hdl_let_memory(&mut self, hdl_let: HdlLet<HdlLetKindMemory>) -> Local {
let name = &hdl_let.name;
let m = hdl_let.kind.m;
let dot = hdl_let.kind.dot_token;
let memory_fn = hdl_let.kind.memory_fn;
let memory_fn_name = memory_fn.name();
self.require_normal_module(memory_fn_name);
let mut expr = quote! {#m #dot #memory_fn_name};
let (paren, arg) = match memory_fn {
MemoryFn::Memory {
memory,
paren,
ty_expr,
} => (paren, unwrap_or_static_type(ty_expr.as_ref(), memory.span())),
2024-06-11 06:09:13 +00:00
MemoryFn::MemoryArray {
memory_array,
paren,
ty_expr,
} => (
paren,
unwrap_or_static_type(ty_expr.as_ref(), memory_array.span()),
2024-06-11 06:09:13 +00:00
),
MemoryFn::MemoryWithInit {
memory_with_init: _,
paren,
init_expr,
} => (paren, quote! { #init_expr }),
};
paren.surround(&mut expr, |expr| {
let name_str = ImplicitName {
name,
span: name.span(),
};
quote_spanned! {name.span()=>
#name_str,
}
.to_tokens(expr);
arg.to_tokens(expr);
});
Local {
attrs: hdl_let.attrs.clone(),
let_token: hdl_let.let_token,
pat: parse_quote_let_pat(&hdl_let.mut_token, name, hdl_let.kind.ty.clone(), |ty| ty),
init: Some(LocalInit {
eq_token: hdl_let.eq_token,
expr: parse_quote! { #expr },
diverge: None,
}),
semi_token: hdl_let.semi_token,
}
}
fn process_hdl_let(&mut self, hdl_let: HdlLet) -> Local {
macro_rules! the_match {
($($Variant:ident => $fn:ident,)*) => {
match hdl_let.kind {
$(
HdlLetKind::$Variant(_) => {
self.$fn(hdl_let.map(|kind| match kind {
HdlLetKind::$Variant(kind) => kind,
_ => unreachable!(),
}))
}
)*
}
};
}
the_match! {
IO => process_hdl_let_io,
Instance => process_hdl_let_instance,
RegBuilder => process_hdl_let_reg_builder,
Wire => process_hdl_let_wire,
Memory => process_hdl_let_memory,
}
}
fn process_int_literal(
&mut self,
span: Span,
base10_digits: &str,
suffix: &str,
) -> Option<Expr> {
let Some(("hdl", suffix)) = suffix.split_once('_') else {
return None;
};
let signed = match suffix.as_bytes().first() {
Some(b'u') => false,
Some(b'i') => true,
_ => {
self.errors.push(Error::new(
span,
"invalid hdl integer suffix -- use something like 123_hdl_u5 or -345_hdl_i20",
));
return None;
}
};
let width: usize = self
.errors
.ok(suffix[1..].parse().map_err(|e| Error::new(span, e)))?;
let value: BigInt = self
.errors
.ok(base10_digits.parse().map_err(|e| Error::new(span, e)))?;
let (negative, bytes) = match value.sign() {
Sign::Minus => (true, value.magnitude().to_bytes_le()),
Sign::NoSign => (false, vec![]),
Sign::Plus => (false, value.magnitude().to_bytes_le()),
};
Some(parse_quote_spanned! {span=>
::fayalite::int::make_int_literal::<#signed, #width>(
#negative,
&[#(#bytes,)*],
).to_int_expr()
2024-06-11 06:09:13 +00:00
})
}
fn process_literal(&mut self, literal: ExprLit) -> Expr {
let ExprLit { attrs, lit } = literal;
match &lit {
Lit::Byte(lit_byte) => {
let trimmed_suffix = lit_byte.suffix().trim_start_matches('_');
if trimmed_suffix == "hdl" || trimmed_suffix == "hdl_u8" {
if let Some(retval) = self.process_int_literal(
lit_byte.span(),
&lit_byte.value().to_string(),
"hdl_u8",
) {
return retval;
}
2024-06-11 06:09:13 +00:00
}
}
Lit::Int(lit_int) => {
if let Some(retval) = self.process_int_literal(
lit_int.span(),
lit_int.base10_digits(),
lit_int.suffix(),
) {
return retval;
}
}
_ => {}
}
fold_expr_lit(self, ExprLit { attrs, lit }).into()
}
fn process_unary(&mut self, unary: ExprUnary) -> Expr {
if let UnOp::Neg(minus_sign) = unary.op {
if let Expr::Lit(ExprLit {
attrs: _,
lit: Lit::Int(lit_int),
}) = &*unary.expr
{
let span = lit_int.span();
let span = minus_sign.span.join(span).unwrap_or(span);
if let Some(retval) = self.process_int_literal(
span,
&format!("-{}", lit_int.base10_digits()),
lit_int.suffix(),
) {
return retval;
}
}
}
fold_expr_unary(self, unary).into()
}
}
fn empty_let() -> Local {
Local {
attrs: vec![],
let_token: Default::default(),
pat: Pat::Type(parse_quote! {_: ()}),
init: None,
semi_token: Default::default(),
}
}
impl Fold for Visitor {
fn fold_item(&mut self, item: Item) -> Item {
// don't process item interiors
item
}
fn fold_generic_argument(&mut self, i: GenericArgument) -> GenericArgument {
// don't process inside generic arguments
i
}
fn fold_attribute(&mut self, attr: Attribute) -> Attribute {
if is_hdl_attr(&attr) {
self.errors
.error(&attr, "#[hdl] attribute not supported here");
}
attr
}
fn fold_expr_repeat(&mut self, i: ExprRepeat) -> ExprRepeat {
let ExprRepeat {
mut attrs,
bracket_token,
mut expr,
semi_token,
len,
} = i;
attrs = attrs
.into_iter()
.map(|attr| self.fold_attribute(attr))
.collect();
*expr = self.fold_expr(*expr);
// don't process inside len, since it's a const context
ExprRepeat {
attrs,
bracket_token,
expr,
semi_token,
len,
}
}
fn fold_expr(&mut self, expr: Expr) -> Expr {
macro_rules! match_hdl_expr {
(
match $expr:ident {
$($Variant:ident => $process_fn:ident,)*
}
) => {
match $expr {
$(Expr::$Variant(mut expr) => {
if let Some(hdl_attr) = self.take_hdl_attr(&mut expr.attrs) {
let expr = expr.do_fold(self);
self.$process_fn(hdl_attr, expr)
} else {
expr.do_fold(self).into()
}
})*
Expr::Lit(literal) => self.process_literal(literal),
Expr::Unary(unary) => self.process_unary(unary),
expr => fold_expr(self, expr),
}
};
}
match_hdl_expr! {
match expr {
If => process_hdl_if,
Match => process_hdl_match,
Array => process_hdl_array,
Repeat => process_hdl_repeat,
2024-06-11 06:09:13 +00:00
Struct => process_hdl_struct,
Tuple => process_hdl_tuple,
Call => process_hdl_call,
Path => process_hdl_path,
}
}
}
fn fold_local(&mut self, let_stmt: Local) -> Local {
match self
.errors
.ok(HdlAttr::<Nothing>::parse_and_leave_attr(&let_stmt.attrs))
{
None => return empty_let(),
Some(None) => return fold_local(self, let_stmt),
Some(Some(HdlAttr { .. })) => {}
};
let hdl_let = syn::parse2::<HdlLet>(let_stmt.into_token_stream());
let Some(hdl_let) = self.errors.ok(hdl_let) else {
return empty_let();
};
let hdl_let = hdl_let.do_fold(self);
self.process_hdl_let(hdl_let)
}
fn fold_stmt(&mut self, stmt: syn::Stmt) -> syn::Stmt {
match stmt {
syn::Stmt::Item(_) => stmt, // don't process inside items
syn::Stmt::Macro(_) => stmt, // don't process inside macros
syn::Stmt::Local(_) | syn::Stmt::Expr(_, _) => fold_stmt(self, stmt),
}
}
fn fold_block(&mut self, i: syn::Block) -> syn::Block {
self.block_depth += 1;
let retval = syn::fold::fold_block(self, i);
self.block_depth -= 1;
retval
}
}
pub(crate) fn transform_body(
module_kind: ModuleKind,
mut body: Box<Block>,
) -> syn::Result<(Box<Block>, Vec<ModuleIO>)> {
let mut visitor = Visitor {
module_kind,
errors: Errors::new(),
io: vec![],
block_depth: 0,
};
*body = syn::fold::fold_block(&mut visitor, *body);
visitor.errors.finish()?;
Ok((body, visitor.io))
}