// 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 { pub(crate) colon_token: Token![:], pub(crate) ty: Box, pub(crate) m: kw::m, pub(crate) dot_token: Token![.], pub(crate) kind: Kind, pub(crate) paren: Paren, pub(crate) ty_expr: Option>, } } pub(crate) fn parse_single_fn_arg(input: ParseStream) -> syn::Result> { let retval = input.parse()?; let _: Option = input.parse()?; Ok(retval) } pub(crate) fn parse_optional_fn_arg(input: ParseStream) -> syn::Result>> { if input.is_empty() { return Ok(None); } parse_single_fn_arg(input).map(Some) } impl HdlLetKindToTokens for HdlLetKindIO { 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, } impl_fold! { struct HdlLetKindInstance<> { m: kw::m, dot_token: Token![.], instance: kw::instance, paren: Paren, module: Box, } } 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, } impl_fold! { struct RegBuilderClockDomain<> { dot_token: Token![.], clock_domain: kw::clock_domain, paren: Paren, expr: Box, } } impl Parse for RegBuilderClockDomain { fn parse(input: ParseStream) -> syn::Result { 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>, }, Reset { dot_token: Token![.], reset: kw::reset, paren: Paren, init_expr: Box, }, 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>, }, Reset { dot_token: Token![.], reset: kw::reset, paren: Paren, init_expr: Box, }, ResetDefault { dot_token: Token![.], reset_default: kw::reset_default, paren: Paren, }, } } impl Parse for RegBuilderReset { fn parse(input: ParseStream) -> syn::Result { 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 { 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)> { 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)>, 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, pub(crate) reset: RegBuilderReset, } impl_fold! { struct HdlLetKindRegBuilder<> { ty: Option<(Token![:], Box)>, m: kw::m, dot_token: Token![.], reg_builder: kw::reg_builder, reg_builder_paren: Paren, clock_domain: Option, reset: RegBuilderReset, } } impl HdlLetKindRegBuilder { fn rest_of_parse( input: ParseStream, parsed_ty: Option<(Token![:], Box)>, _after_ty: Token![=], m: kw::m, dot_token: Token![.], reg_builder: kw::reg_builder, ) -> syn::Result { 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)>, pub(crate) m: kw::m, pub(crate) dot_token: Token![.], pub(crate) wire: kw::wire, pub(crate) paren: Paren, pub(crate) ty_expr: Option>, } impl_fold! { struct HdlLetKindWire<> { ty: Option<(Token![:], Box)>, m: kw::m, dot_token: Token![.], wire: kw::wire, paren: Paren, ty_expr: Option>, } } 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>, }, MemoryArray { memory_array: kw::memory_array, paren: Paren, ty_expr: Option>, }, MemoryWithInit { memory_with_init: kw::memory_with_init, paren: Paren, init_expr: Box, }, } impl_fold! { enum MemoryFn<> { Memory { memory: kw::memory, paren: Paren, ty_expr: Option>, }, MemoryArray { memory_array: kw::memory_array, paren: Paren, ty_expr: Option>, }, MemoryWithInit { memory_with_init: kw::memory_with_init, paren: Paren, init_expr: Box, }, } } 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 { 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)>, pub(crate) m: kw::m, pub(crate) dot_token: Token![.], pub(crate) memory_fn: MemoryFn, } impl_fold! { struct HdlLetKindMemory<> { ty: Option<(Token![:], Box)>, 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)>, _after_ty: Token![=], m: kw::m, dot_token: Token![.], memory_fn_name: MemoryFnName, ) -> syn::Result { 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)>, after_ty: Token![=], ) -> syn::Result<(Token![:], Box)> { 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)>, after_ty: Token![=], m: kw::m, dot_token: Token![.], kind: ModuleIOKind, ) -> syn::Result { 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)>; fn parse_ty(input: ParseStream) -> syn::Result { 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 { 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 { pub(crate) attrs: Vec, pub(crate) hdl_attr: HdlAttr, pub(crate) let_token: Token![let], pub(crate) mut_token: Option, pub(crate) name: Ident, pub(crate) eq_token: Token![=], pub(crate) kind: Kind, pub(crate) semi_token: Token![;], } } impl HdlLet { pub(crate) fn try_map( self, f: impl FnOnce(Kind) -> Result, ) -> Result, 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(self, f: impl FnOnce(Kind) -> Kind2) -> HdlLet { match self.try_map(|kind| Ok::(f(kind))) { Ok(v) => v, Err(e) => match e {}, } } } pub trait HdlLetKindParse: Sized { type ParsedTy; fn parse_ty(input: ParseStream) -> syn::Result; fn parse_expr( name: &Ident, parsed_ty: Self::ParsedTy, after_ty: Token![=], input: ParseStream, ) -> syn::Result; } pub trait HdlLetKindToTokens { fn ty_to_tokens(&self, tokens: &mut TokenStream); fn expr_to_tokens(&self, tokens: &mut TokenStream); } impl Parse for HdlLet { fn parse(input: ParseStream) -> syn::Result { 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 ToTokens for HdlLet { 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>( mut_token: &Option, 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_fixed_type(expr: Option, span: Span) -> TokenStream { expr.map(ToTokens::into_token_stream).unwrap_or_else(|| { quote_spanned! {span=> ::fayalite::ty::FixedType::fixed_type() } }) } struct ImplicitName { name: T, span: Span, } impl ToTokens for ImplicitName { 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, block_depth: usize, } impl Visitor { fn take_hdl_attr(&mut self, attrs: &mut Vec) -> Option> { self.errors.unwrap_or( HdlAttr::parse_and_take_attr(attrs), Some(syn::parse2::(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, 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) -> 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_fixed_type(hdl_let.kind.ty_expr.as_ref(), kind.span()); 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) -> 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) -> 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_fixed_type(ty_expr.as_ref(), reg_builder.span()); 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) -> 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_fixed_type(hdl_let.kind.ty_expr.as_ref(), wire.span()); 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) -> 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_fixed_type(ty_expr.as_ref(), memory.span())), MemoryFn::MemoryArray { memory_array, paren, ty_expr, } => ( paren, unwrap_or_fixed_type(ty_expr.as_ref(), memory_array.span()), ), 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 { 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() }) } fn process_literal(&mut self, literal: ExprLit) -> Expr { let ExprLit { attrs, lit } = literal; match &lit { Lit::Byte(lit_byte) if lit_byte.suffix() == "hdl" => { if let Some(retval) = self.process_int_literal( lit_byte.span(), &lit_byte.value().to_string(), "hdl_u8", ) { return retval; } } 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, 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::::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::(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, ) -> syn::Result<(Box, Vec)> { 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)) }