forked from libre-chip/fayalite
1664 lines
50 KiB
Rust
1664 lines
50 KiB
Rust
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
// See Notices.txt for copyright information
|
|
use crate::{
|
|
fold::{impl_fold, DoFold},
|
|
hdl_type_common::{
|
|
known_items, ParseFailed, ParseTypes, ParsedGenerics, ParsedType, TypesParser,
|
|
},
|
|
is_hdl_attr, kw,
|
|
module::{check_name_conflicts_with_module_builder, ModuleIO, ModuleIOKind, ModuleKind},
|
|
options, Errors, HdlAttr,
|
|
};
|
|
use num_bigint::BigInt;
|
|
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, Ty = ParsedType> {
|
|
pub(crate) colon_token: Token![:],
|
|
pub(crate) ty: Box<Ty>,
|
|
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>>,
|
|
}
|
|
}
|
|
|
|
impl<Kind: Clone, T: ParseTypes<I>, I> ParseTypes<HdlLetKindIO<Kind, I>> for HdlLetKindIO<Kind, T> {
|
|
fn parse_types(
|
|
input: &mut HdlLetKindIO<Kind, I>,
|
|
parser: &mut TypesParser<'_>,
|
|
) -> Result<Self, ParseFailed> {
|
|
let HdlLetKindIO {
|
|
colon_token,
|
|
ty,
|
|
m,
|
|
dot_token,
|
|
kind,
|
|
paren,
|
|
ty_expr,
|
|
} = input;
|
|
Ok(Self {
|
|
colon_token: *colon_token,
|
|
ty: ParseTypes::parse_types(ty, parser)?,
|
|
m: *m,
|
|
dot_token: *dot_token,
|
|
kind: kind.clone(),
|
|
paren: *paren,
|
|
ty_expr: ty_expr.clone(),
|
|
})
|
|
}
|
|
}
|
|
|
|
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) instance: kw::instance,
|
|
pub(crate) paren: Paren,
|
|
pub(crate) module: Box<Expr>,
|
|
}
|
|
|
|
impl ParseTypes<Self> for HdlLetKindInstance {
|
|
fn parse_types(input: &mut Self, _parser: &mut TypesParser<'_>) -> Result<Self, ParseFailed> {
|
|
Ok(input.clone())
|
|
}
|
|
}
|
|
|
|
impl_fold! {
|
|
struct HdlLetKindInstance<> {
|
|
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 {
|
|
instance,
|
|
paren,
|
|
module,
|
|
} = self;
|
|
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)> {
|
|
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) reg_builder: kw::reg_builder,
|
|
pub(crate) reg_builder_paren: Paren,
|
|
pub(crate) clock_domain: Option<RegBuilderClockDomain>,
|
|
pub(crate) reset: RegBuilderReset,
|
|
}
|
|
|
|
impl ParseTypes<Self> for HdlLetKindRegBuilder {
|
|
fn parse_types(input: &mut Self, _parser: &mut TypesParser<'_>) -> Result<Self, ParseFailed> {
|
|
Ok(input.clone())
|
|
}
|
|
}
|
|
|
|
impl_fold! {
|
|
struct HdlLetKindRegBuilder<> {
|
|
ty: Option<(Token![:], Box<Type>)>,
|
|
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_dot: Option<(kw::m, Token![.])>,
|
|
reg_builder: kw::reg_builder,
|
|
) -> syn::Result<Self> {
|
|
check_empty_m_dot(m_dot, reg_builder)?;
|
|
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,
|
|
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: _,
|
|
reg_builder,
|
|
reg_builder_paren,
|
|
clock_domain,
|
|
reset,
|
|
} = self;
|
|
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) wire: kw::wire,
|
|
pub(crate) paren: Paren,
|
|
pub(crate) ty_expr: Option<Box<Expr>>,
|
|
}
|
|
|
|
impl ParseTypes<Self> for HdlLetKindWire {
|
|
fn parse_types(input: &mut Self, _parser: &mut TypesParser<'_>) -> Result<Self, ParseFailed> {
|
|
Ok(input.clone())
|
|
}
|
|
}
|
|
|
|
impl_fold! {
|
|
struct HdlLetKindWire<> {
|
|
ty: Option<(Token![:], Box<Type>)>,
|
|
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: _,
|
|
wire,
|
|
paren,
|
|
ty_expr,
|
|
} = self;
|
|
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) memory_fn: MemoryFn,
|
|
}
|
|
|
|
impl ParseTypes<Self> for HdlLetKindMemory {
|
|
fn parse_types(input: &mut Self, _parser: &mut TypesParser<'_>) -> Result<Self, ParseFailed> {
|
|
Ok(input.clone())
|
|
}
|
|
}
|
|
|
|
impl_fold! {
|
|
struct HdlLetKindMemory<> {
|
|
ty: Option<(Token![:], Box<Type>)>,
|
|
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: _, memory_fn } = self;
|
|
memory_fn.to_tokens(tokens);
|
|
}
|
|
}
|
|
|
|
impl HdlLetKindMemory {
|
|
fn rest_of_parse(
|
|
input: ParseStream,
|
|
parsed_ty: Option<(Token![:], Box<Type>)>,
|
|
_after_ty: Token![=],
|
|
m_dot: Option<(kw::m, Token![.])>,
|
|
memory_fn_name: MemoryFnName,
|
|
) -> syn::Result<Self> {
|
|
check_empty_m_dot(m_dot, memory_fn_name)?;
|
|
Ok(Self {
|
|
ty: parsed_ty,
|
|
memory_fn: MemoryFn::parse_rest(input, memory_fn_name)?,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub(crate) enum HdlLetKind<IOType = ParsedType> {
|
|
IO(HdlLetKindIO<ModuleIOKind, IOType>),
|
|
Instance(HdlLetKindInstance),
|
|
RegBuilder(HdlLetKindRegBuilder),
|
|
Wire(HdlLetKindWire),
|
|
Memory(HdlLetKindMemory),
|
|
}
|
|
|
|
impl_fold! {
|
|
enum HdlLetKind<IOType,> {
|
|
IO(HdlLetKindIO<ModuleIOKind, IOType>),
|
|
Instance(HdlLetKindInstance),
|
|
RegBuilder(HdlLetKindRegBuilder),
|
|
Wire(HdlLetKindWire),
|
|
Memory(HdlLetKindMemory),
|
|
}
|
|
}
|
|
|
|
impl<T: ParseTypes<I>, I> ParseTypes<HdlLetKind<I>> for HdlLetKind<T> {
|
|
fn parse_types(
|
|
input: &mut HdlLetKind<I>,
|
|
parser: &mut TypesParser<'_>,
|
|
) -> Result<Self, ParseFailed> {
|
|
match input {
|
|
HdlLetKind::IO(input) => ParseTypes::parse_types(input, parser).map(HdlLetKind::IO),
|
|
HdlLetKind::Instance(input) => {
|
|
ParseTypes::parse_types(input, parser).map(HdlLetKind::Instance)
|
|
}
|
|
HdlLetKind::RegBuilder(input) => {
|
|
ParseTypes::parse_types(input, parser).map(HdlLetKind::RegBuilder)
|
|
}
|
|
HdlLetKind::Wire(input) => ParseTypes::parse_types(input, parser).map(HdlLetKind::Wire),
|
|
HdlLetKind::Memory(input) => {
|
|
ParseTypes::parse_types(input, parser).map(HdlLetKind::Memory)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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<ModuleIOKind, Type> {
|
|
fn rest_of_parse(
|
|
input: ParseStream,
|
|
parsed_ty: Option<(Token![:], Box<Type>)>,
|
|
after_ty: Token![=],
|
|
m_dot: Option<(kw::m, Token![.])>,
|
|
kind: ModuleIOKind,
|
|
) -> syn::Result<Self> {
|
|
let (m, dot_token) = unwrap_m_dot(m_dot, kind)?;
|
|
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)?,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn check_empty_m_dot(m_dot: Option<(kw::m, Token![.])>, kind: impl ToTokens) -> syn::Result<()> {
|
|
if let Some((m, dot_token)) = m_dot {
|
|
Err(Error::new_spanned(
|
|
quote! {#m #dot_token #kind},
|
|
format_args!(
|
|
"{} is a free function, not a method of ModuleBuilder: try removing the `m.`",
|
|
kind.to_token_stream()
|
|
),
|
|
))
|
|
} else {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
fn unwrap_m_dot(
|
|
m_dot: Option<(kw::m, Token![.])>,
|
|
kind: impl ToTokens,
|
|
) -> syn::Result<(kw::m, Token![.])> {
|
|
m_dot.ok_or_else(|| {
|
|
Error::new_spanned(
|
|
&kind,
|
|
format_args!(
|
|
"{} is a ModuleBuilder method, not a free function: try prefixing it with `m.`",
|
|
kind.to_token_stream()
|
|
),
|
|
)
|
|
})
|
|
}
|
|
|
|
impl HdlLetKindParse for HdlLetKind<Type> {
|
|
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_dot = if input.peek(kw::m) && input.peek2(Token![.]) {
|
|
let m = input.parse()?;
|
|
let dot_token = input.parse()?;
|
|
Some((m, dot_token))
|
|
} else {
|
|
None
|
|
};
|
|
let kind: LetFnKind = input.parse()?;
|
|
match kind {
|
|
LetFnKind::Input(input_token) => HdlLetKindIO::rest_of_parse(
|
|
input,
|
|
parsed_ty,
|
|
after_ty,
|
|
m_dot,
|
|
ModuleIOKind::Input(input_token),
|
|
)
|
|
.map(Self::IO),
|
|
LetFnKind::Output(output) => HdlLetKindIO::rest_of_parse(
|
|
input,
|
|
parsed_ty,
|
|
after_ty,
|
|
m_dot,
|
|
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",
|
|
));
|
|
}
|
|
check_empty_m_dot(m_dot, kind)?;
|
|
let paren_contents;
|
|
Ok(Self::Instance(HdlLetKindInstance {
|
|
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, reg_builder)
|
|
.map(Self::RegBuilder)
|
|
}
|
|
LetFnKind::Wire((wire,)) => {
|
|
check_empty_m_dot(m_dot, wire)?;
|
|
let paren_contents;
|
|
Ok(Self::Wire(HdlLetKindWire {
|
|
ty: parsed_ty,
|
|
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,
|
|
MemoryFnName::Memory(fn_name),
|
|
)
|
|
.map(Self::Memory),
|
|
LetFnKind::MemoryArray(fn_name) => HdlLetKindMemory::rest_of_parse(
|
|
input,
|
|
parsed_ty,
|
|
after_ty,
|
|
m_dot,
|
|
MemoryFnName::MemoryArray(fn_name),
|
|
)
|
|
.map(Self::Memory),
|
|
LetFnKind::MemoryWithInit(fn_name) => HdlLetKindMemory::rest_of_parse(
|
|
input,
|
|
parsed_ty,
|
|
after_ty,
|
|
m_dot,
|
|
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<T: ParseTypes<I>, I> ParseTypes<HdlLet<I>> for HdlLet<T> {
|
|
fn parse_types(
|
|
input: &mut HdlLet<I>,
|
|
parser: &mut TypesParser<'_>,
|
|
) -> Result<Self, ParseFailed> {
|
|
let HdlLet {
|
|
attrs,
|
|
hdl_attr,
|
|
let_token,
|
|
mut_token,
|
|
name,
|
|
eq_token,
|
|
kind,
|
|
semi_token,
|
|
} = input;
|
|
Ok(Self {
|
|
attrs: attrs.clone(),
|
|
hdl_attr: hdl_attr.clone(),
|
|
let_token: *let_token,
|
|
mut_token: *mut_token,
|
|
name: name.clone(),
|
|
eq_token: *eq_token,
|
|
kind: T::parse_types(kind, parser)?,
|
|
semi_token: *semi_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 {
|
|
expr.map(ToTokens::into_token_stream).unwrap_or_else(|| {
|
|
quote_spanned! {span=>
|
|
::fayalite::ty::StaticType::TYPE
|
|
}
|
|
})
|
|
}
|
|
|
|
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<'a> {
|
|
module_kind: ModuleKind,
|
|
errors: Errors,
|
|
io: Vec<ModuleIO>,
|
|
block_depth: usize,
|
|
parsed_generics: &'a ParsedGenerics,
|
|
}
|
|
|
|
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 = ::fayalite::module::if_(#cond);
|
|
let _: () = #then_branch;
|
|
let mut __scope = __scope.else_();
|
|
let _: () = #else_expr;
|
|
}
|
|
}
|
|
} else {
|
|
parse_quote_spanned! {if_token.span=>
|
|
#(#attrs)*
|
|
{
|
|
let mut __scope = ::fayalite::module::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());
|
|
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 {
|
|
instance,
|
|
paren,
|
|
module,
|
|
},
|
|
semi_token,
|
|
} = hdl_let;
|
|
self.require_normal_module(instance);
|
|
let mut expr = instance.to_token_stream();
|
|
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 reg_builder = hdl_let.kind.reg_builder;
|
|
self.require_normal_module(reg_builder);
|
|
let mut expr = reg_builder.to_token_stream();
|
|
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());
|
|
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 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());
|
|
let mut expr = wire.to_token_stream();
|
|
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 memory_fn = hdl_let.kind.memory_fn;
|
|
let memory_fn_name = memory_fn.name();
|
|
self.require_normal_module(memory_fn_name);
|
|
let mut expr = memory_fn_name.to_token_stream();
|
|
let (paren, arg) = match memory_fn {
|
|
MemoryFn::Memory {
|
|
memory,
|
|
paren,
|
|
ty_expr,
|
|
} => (
|
|
paren,
|
|
unwrap_or_static_type(ty_expr.as_ref(), memory.span()),
|
|
),
|
|
MemoryFn::MemoryArray {
|
|
memory_array,
|
|
paren,
|
|
ty_expr,
|
|
} => (
|
|
paren,
|
|
unwrap_or_static_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<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 bytes = value.to_signed_bytes_le();
|
|
let path = if signed {
|
|
known_items::SInt(span).path
|
|
} else {
|
|
known_items::UInt(span).path
|
|
};
|
|
Some(parse_quote_spanned! {span=>
|
|
<#path<#width> as ::fayalite::int::BoolOrIntType>::le_bytes_to_expr_wrapping(
|
|
&[#(#bytes,)*],
|
|
#width,
|
|
)
|
|
})
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
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,
|
|
Struct => process_hdl_struct,
|
|
Tuple => process_hdl_tuple,
|
|
}
|
|
}
|
|
}
|
|
|
|
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<HdlLetKind<Type>>>(let_stmt.into_token_stream());
|
|
let Some(hdl_let) = self.errors.ok(hdl_let) else {
|
|
return empty_let();
|
|
};
|
|
let mut hdl_let = hdl_let.do_fold(self);
|
|
let Ok(hdl_let) =
|
|
TypesParser::run_with_errors(self.parsed_generics, &mut hdl_let, &mut self.errors)
|
|
else {
|
|
return empty_let();
|
|
};
|
|
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>,
|
|
parsed_generics: &ParsedGenerics,
|
|
) -> syn::Result<(Box<Block>, Vec<ModuleIO>)> {
|
|
let mut visitor = Visitor {
|
|
module_kind,
|
|
errors: Errors::new(),
|
|
io: vec![],
|
|
block_depth: 0,
|
|
parsed_generics,
|
|
};
|
|
*body = syn::fold::fold_block(&mut visitor, *body);
|
|
visitor.errors.finish()?;
|
|
Ok((body, visitor.io))
|
|
}
|