WIP: use HdlOption[the_type_var] or UInt[123 + n] for creating types
All checks were successful
/ test (push) Successful in 4m56s

This commit is contained in:
Jacob Lifshay 2024-08-07 03:16:29 -07:00
parent cd99dbc849
commit 5835b995a9
Signed by: programmerjake
SSH key fingerprint: SHA256:B1iRVvUJkvd7upMIiMqn6OyxvD2SgJkAH3ZnUOj6z+c
63 changed files with 13500 additions and 13210 deletions

View file

@ -2,11 +2,14 @@
// 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, Sign};
use num_bigint::BigInt;
use proc_macro2::{Span, TokenStream};
use quote::{quote, quote_spanned, ToTokens};
use std::{borrow::Borrow, convert::Infallible};
@ -87,9 +90,9 @@ macro_rules! with_debug_clone_and_fold {
pub(crate) use with_debug_clone_and_fold;
with_debug_clone_and_fold! {
pub(crate) struct HdlLetKindIO<Kind = ModuleIOKind> {
pub(crate) struct HdlLetKindIO<Kind = ModuleIOKind, Ty = ParsedType> {
pub(crate) colon_token: Token![:],
pub(crate) ty: Box<Type>,
pub(crate) ty: Box<Ty>,
pub(crate) m: kw::m,
pub(crate) dot_token: Token![.],
pub(crate) kind: Kind,
@ -98,6 +101,32 @@ with_debug_clone_and_fold! {
}
}
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()?;
@ -145,17 +174,19 @@ impl<Kind: ToTokens> HdlLetKindToTokens for HdlLetKindIO<Kind> {
#[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 ParseTypes<Self> for HdlLetKindInstance {
fn parse_types(input: &mut Self, _parser: &mut TypesParser<'_>) -> Result<Self, ParseFailed> {
Ok(input.clone())
}
}
impl_fold! {
struct HdlLetKindInstance<> {
m: kw::m,
dot_token: Token![.],
instance: kw::instance,
paren: Paren,
module: Box<Expr>,
@ -167,14 +198,10 @@ impl HdlLetKindToTokens for HdlLetKindInstance {
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));
}
@ -381,19 +408,21 @@ make_builder_method_enum! {
#[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 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>)>,
m: kw::m,
dot_token: Token![.],
reg_builder: kw::reg_builder,
reg_builder_paren: Paren,
clock_domain: Option<RegBuilderClockDomain>,
@ -406,10 +435,10 @@ impl HdlLetKindRegBuilder {
input: ParseStream,
parsed_ty: Option<(Token![:], Box<Type>)>,
_after_ty: Token![=],
m: kw::m,
dot_token: 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;
@ -430,8 +459,6 @@ impl HdlLetKindRegBuilder {
}
Ok(Self {
ty: parsed_ty,
m,
dot_token,
reg_builder,
reg_builder_paren,
clock_domain,
@ -451,15 +478,11 @@ impl HdlLetKindToTokens for HdlLetKindRegBuilder {
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);
@ -470,18 +493,20 @@ impl HdlLetKindToTokens for HdlLetKindRegBuilder {
#[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 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>)>,
m: kw::m,
dot_token: Token![.],
wire: kw::wire,
paren: Paren,
ty_expr: Option<Box<Expr>>,
@ -499,14 +524,10 @@ impl HdlLetKindToTokens for HdlLetKindWire {
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));
}
@ -627,16 +648,18 @@ impl ToTokens for MemoryFn {
#[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 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>)>,
m: kw::m,
dot_token: Token![.],
memory_fn: MemoryFn,
}
}
@ -650,14 +673,7 @@ impl HdlLetKindToTokens for HdlLetKindMemory {
}
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);
let Self { ty: _, memory_fn } = self;
memory_fn.to_tokens(tokens);
}
}
@ -667,22 +683,20 @@ impl HdlLetKindMemory {
input: ParseStream,
parsed_ty: Option<(Token![:], Box<Type>)>,
_after_ty: Token![=],
m: kw::m,
dot_token: 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,
m,
dot_token,
memory_fn: MemoryFn::parse_rest(input, memory_fn_name)?,
})
}
}
#[derive(Clone, Debug)]
pub(crate) enum HdlLetKind {
IO(HdlLetKindIO),
pub(crate) enum HdlLetKind<IOType = ParsedType> {
IO(HdlLetKindIO<ModuleIOKind, IOType>),
Instance(HdlLetKindInstance),
RegBuilder(HdlLetKindRegBuilder),
Wire(HdlLetKindWire),
@ -690,8 +704,8 @@ pub(crate) enum HdlLetKind {
}
impl_fold! {
enum HdlLetKind<> {
IO(HdlLetKindIO),
enum HdlLetKind<IOType,> {
IO(HdlLetKindIO<ModuleIOKind, IOType>),
Instance(HdlLetKindInstance),
RegBuilder(HdlLetKindRegBuilder),
Wire(HdlLetKindWire),
@ -699,6 +713,27 @@ impl_fold! {
}
}
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![=],
@ -710,15 +745,15 @@ fn parsed_ty_or_err(
}
}
impl HdlLetKindIO {
impl HdlLetKindIO<ModuleIOKind, Type> {
fn rest_of_parse(
input: ParseStream,
parsed_ty: Option<(Token![:], Box<Type>)>,
after_ty: Token![=],
m: kw::m,
dot_token: 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 {
@ -733,7 +768,36 @@ impl HdlLetKindIO {
}
}
impl HdlLetKindParse for HdlLetKind {
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> {
@ -753,16 +817,20 @@ impl HdlLetKindParse for HdlLetKind {
after_ty: Token![=],
input: ParseStream,
) -> syn::Result<Self> {
let m = input.parse()?;
let dot_token = input.parse()?;
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_token,
m_dot,
ModuleIOKind::Input(input_token),
)
.map(Self::IO),
@ -770,8 +838,7 @@ impl HdlLetKindParse for HdlLetKind {
input,
parsed_ty,
after_ty,
m,
dot_token,
m_dot,
ModuleIOKind::Output(output),
)
.map(Self::IO),
@ -782,30 +849,23 @@ impl HdlLetKindParse for HdlLetKind {
"type annotation not allowed for instance",
));
}
check_empty_m_dot(m_dot, kind)?;
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::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,
m,
dot_token,
wire,
paren: parenthesized!(paren_contents in input),
ty_expr: paren_contents.call(parse_optional_fn_arg)?,
@ -815,8 +875,7 @@ impl HdlLetKindParse for HdlLetKind {
input,
parsed_ty,
after_ty,
m,
dot_token,
m_dot,
MemoryFnName::Memory(fn_name),
)
.map(Self::Memory),
@ -824,8 +883,7 @@ impl HdlLetKindParse for HdlLetKind {
input,
parsed_ty,
after_ty,
m,
dot_token,
m_dot,
MemoryFnName::MemoryArray(fn_name),
)
.map(Self::Memory),
@ -833,8 +891,7 @@ impl HdlLetKindParse for HdlLetKind {
input,
parsed_ty,
after_ty,
m,
dot_token,
m_dot,
MemoryFnName::MemoryWithInit(fn_name),
)
.map(Self::Memory),
@ -878,6 +935,34 @@ with_debug_clone_and_fold! {
}
}
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,
@ -1006,7 +1091,7 @@ fn wrap_ty_with_expr(ty: impl ToTokens) -> Type {
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::static_type()
::fayalite::ty::StaticType::TYPE
}
})
}
@ -1026,14 +1111,15 @@ impl<T: ToString> ToTokens for ImplicitName<T> {
}
}
struct Visitor {
struct Visitor<'a> {
module_kind: ModuleKind,
errors: Errors,
io: Vec<ModuleIO>,
block_depth: usize,
parsed_generics: &'a ParsedGenerics,
}
impl Visitor {
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),
@ -1086,7 +1172,7 @@ impl Visitor {
parse_quote_spanned! {if_token.span=>
#(#attrs)*
{
let mut __scope = m.if_(#cond);
let mut __scope = ::fayalite::module::if_(#cond);
let _: () = #then_branch;
let mut __scope = __scope.else_();
let _: () = #else_expr;
@ -1096,7 +1182,7 @@ impl Visitor {
parse_quote_spanned! {if_token.span=>
#(#attrs)*
{
let mut __scope = m.if_(#cond);
let mut __scope = ::fayalite::module::if_(#cond);
let _: () = #then_branch;
}
}
@ -1157,8 +1243,6 @@ impl Visitor {
eq_token,
kind:
HdlLetKindInstance {
m,
dot_token,
instance,
paren,
module,
@ -1166,7 +1250,7 @@ impl Visitor {
semi_token,
} = hdl_let;
self.require_normal_module(instance);
let mut expr = quote! {#m #dot_token #instance};
let mut expr = instance.to_token_stream();
paren.surround(&mut expr, |expr| {
let name_str = ImplicitName {
name: &name,
@ -1191,11 +1275,9 @@ impl Visitor {
}
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};
let mut expr = reg_builder.to_token_stream();
hdl_let.kind.reg_builder_paren.surround(&mut expr, |expr| {
let name_str = ImplicitName {
name,
@ -1244,12 +1326,10 @@ impl Visitor {
}
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());
let mut expr = quote! {#m #dot #wire};
let mut expr = wire.to_token_stream();
hdl_let.kind.paren.surround(&mut expr, |expr| {
let name_str = ImplicitName {
name,
@ -1279,18 +1359,19 @@ impl Visitor {
}
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 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())),
} => (
paren,
unwrap_or_static_type(ty_expr.as_ref(), memory.span()),
),
MemoryFn::MemoryArray {
memory_array,
paren,
@ -1377,16 +1458,17 @@ impl Visitor {
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()),
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=>
::fayalite::int::make_int_literal::<#signed, #width>(
#negative,
<#path<#width> as ::fayalite::int::BoolOrIntType>::le_bytes_to_expr_wrapping(
&[#(#bytes,)*],
).to_int_expr()
#width,
)
})
}
fn process_literal(&mut self, literal: ExprLit) -> Expr {
@ -1449,7 +1531,7 @@ fn empty_let() -> Local {
}
}
impl Fold for Visitor {
impl Fold for Visitor<'_> {
fn fold_item(&mut self, item: Item) -> Item {
// don't process item interiors
item
@ -1521,8 +1603,6 @@ impl Fold for Visitor {
Repeat => process_hdl_repeat,
Struct => process_hdl_struct,
Tuple => process_hdl_tuple,
Call => process_hdl_call,
Path => process_hdl_path,
}
}
}
@ -1536,11 +1616,16 @@ impl Fold for Visitor {
Some(None) => return fold_local(self, let_stmt),
Some(Some(HdlAttr { .. })) => {}
};
let hdl_let = syn::parse2::<HdlLet>(let_stmt.into_token_stream());
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 hdl_let = hdl_let.do_fold(self);
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)
}
@ -1563,12 +1648,14 @@ impl Fold for Visitor {
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()?;

View file

@ -1,336 +1,13 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{module::transform_body::Visitor, options, Errors, HdlAttr, PairsIterExt};
use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote_spanned, ToTokens, TokenStreamExt};
use crate::{module::transform_body::Visitor, HdlAttr};
use quote::{format_ident, quote_spanned};
use syn::{
parse::Nothing,
parse_quote, parse_quote_spanned,
punctuated::{Pair, Punctuated},
spanned::Spanned,
token::{Brace, Paren},
Attribute, Expr, ExprArray, ExprCall, ExprGroup, ExprPath, ExprRepeat, ExprStruct, ExprTuple,
FieldValue, Ident, Index, Member, Path, PathArguments, PathSegment, Token, TypePath,
parse::Nothing, parse_quote, parse_quote_spanned, spanned::Spanned, Expr, ExprArray, ExprPath,
ExprRepeat, ExprStruct, ExprTuple, FieldValue, TypePath,
};
options! {
#[options = AggregateLiteralOptions]
#[no_ident_fragment]
pub(crate) enum AggregateLiteralOption {
Struct(struct_),
Enum(enum_),
}
}
#[derive(Clone, Debug)]
pub(crate) struct StructOrEnumPath {
pub(crate) ty: TypePath,
pub(crate) variant: Option<(TypePath, Ident)>,
}
#[derive(Debug, Copy, Clone)]
pub(crate) struct SingleSegmentVariant {
pub(crate) name: &'static str,
pub(crate) make_type_path: fn(Span, &PathArguments) -> Path,
}
impl StructOrEnumPath {
pub(crate) const SINGLE_SEGMENT_VARIANTS: &'static [SingleSegmentVariant] = {
fn make_option_type_path(span: Span, arguments: &PathArguments) -> Path {
let arguments = if arguments.is_none() {
quote_spanned! {span=>
<_>
}
} else {
arguments.to_token_stream()
};
parse_quote_spanned! {span=>
::fayalite::__std::option::Option #arguments
}
}
fn make_result_type_path(span: Span, arguments: &PathArguments) -> Path {
let arguments = if arguments.is_none() {
quote_spanned! {span=>
<_, _>
}
} else {
arguments.to_token_stream()
};
parse_quote_spanned! {span=>
::fayalite::__std::result::Result #arguments
}
}
&[
SingleSegmentVariant {
name: "Some",
make_type_path: make_option_type_path,
},
SingleSegmentVariant {
name: "None",
make_type_path: make_option_type_path,
},
SingleSegmentVariant {
name: "Ok",
make_type_path: make_result_type_path,
},
SingleSegmentVariant {
name: "Err",
make_type_path: make_result_type_path,
},
]
};
pub(crate) fn new(
errors: &mut Errors,
path: TypePath,
options: &AggregateLiteralOptions,
) -> Result<Self, ()> {
let Path {
leading_colon,
segments,
} = &path.path;
let qself_position = path.qself.as_ref().map(|qself| qself.position).unwrap_or(0);
let variant_name = if qself_position < segments.len() {
Some(segments.last().unwrap().ident.clone())
} else {
None
};
let enum_type = 'guess_enum_type: {
if options.enum_.is_some() {
if let Some((struct_,)) = options.struct_ {
errors.error(
struct_,
"can't specify both #[hdl(enum)] and #[hdl(struct)]",
);
}
break 'guess_enum_type Some(None);
}
if options.struct_.is_some() {
break 'guess_enum_type None;
}
if path.qself.is_none() && leading_colon.is_none() && segments.len() == 1 {
let PathSegment { ident, arguments } = &segments[0];
for &SingleSegmentVariant {
name,
make_type_path,
} in Self::SINGLE_SEGMENT_VARIANTS
{
if ident == name {
break 'guess_enum_type Some(Some(TypePath {
qself: None,
path: make_type_path(ident.span(), arguments),
}));
}
}
}
if segments.len() == qself_position + 2
&& segments[qself_position + 1].arguments.is_none()
&& (path.qself.is_some()
|| segments[qself_position].ident.to_string().as_bytes()[0]
.is_ascii_uppercase())
{
let mut ty = path.clone();
ty.path.segments.pop();
ty.path.segments.pop_punct();
break 'guess_enum_type Some(Some(ty));
}
None
};
if let Some(enum_type) = enum_type {
let ty = if let Some(enum_type) = enum_type {
enum_type
} else {
if qself_position >= segments.len() {
errors.error(path, "#[hdl]: can't figure out enum's type");
return Err(());
}
let mut ty = path.clone();
ty.path.segments.pop();
ty.path.segments.pop_punct();
ty
};
let Some(variant_name) = variant_name else {
errors.error(path, "#[hdl]: can't figure out enum's variant name");
return Err(());
};
Ok(Self {
ty,
variant: Some((path, variant_name)),
})
} else {
Ok(Self {
ty: path,
variant: None,
})
}
}
}
#[derive(Copy, Clone, Debug)]
pub(crate) enum BraceOrParen {
Brace(Brace),
Paren(Paren),
}
impl BraceOrParen {
pub(crate) fn surround(self, tokens: &mut TokenStream, f: impl FnOnce(&mut TokenStream)) {
match self {
BraceOrParen::Brace(v) => v.surround(tokens, f),
BraceOrParen::Paren(v) => v.surround(tokens, f),
}
}
}
#[derive(Debug, Clone)]
pub(crate) struct StructOrEnumLiteralField {
pub(crate) attrs: Vec<Attribute>,
pub(crate) member: Member,
pub(crate) colon_token: Option<Token![:]>,
pub(crate) expr: Expr,
}
#[derive(Debug, Clone)]
pub(crate) struct StructOrEnumLiteral {
pub(crate) attrs: Vec<Attribute>,
pub(crate) path: TypePath,
pub(crate) brace_or_paren: BraceOrParen,
pub(crate) fields: Punctuated<StructOrEnumLiteralField, Token![,]>,
pub(crate) dot2_token: Option<Token![..]>,
pub(crate) rest: Option<Box<Expr>>,
}
impl StructOrEnumLiteral {
pub(crate) fn map_field_exprs(self, mut f: impl FnMut(Expr) -> Expr) -> Self {
self.map_fields(|mut field| {
field.expr = f(field.expr);
field
})
}
pub(crate) fn map_fields(
self,
f: impl FnMut(StructOrEnumLiteralField) -> StructOrEnumLiteralField,
) -> Self {
let Self {
attrs,
path,
brace_or_paren,
fields,
dot2_token,
rest,
} = self;
let fields = fields.into_pairs().map_pair_value(f).collect();
Self {
attrs,
path,
brace_or_paren,
fields,
dot2_token,
rest,
}
}
}
impl From<ExprStruct> for StructOrEnumLiteral {
fn from(value: ExprStruct) -> Self {
let ExprStruct {
attrs,
qself,
path,
brace_token,
fields,
dot2_token,
rest,
} = value;
Self {
attrs,
path: TypePath { qself, path },
brace_or_paren: BraceOrParen::Brace(brace_token),
fields: fields
.into_pairs()
.map_pair_value(
|FieldValue {
attrs,
member,
colon_token,
expr,
}| StructOrEnumLiteralField {
attrs,
member,
colon_token,
expr,
},
)
.collect(),
dot2_token,
rest,
}
}
}
fn expr_to_member(expr: &Expr) -> Option<Member> {
syn::parse2(expr.to_token_stream()).ok()
}
impl ToTokens for StructOrEnumLiteral {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self {
attrs,
path,
brace_or_paren,
fields,
dot2_token,
rest,
} = self;
tokens.append_all(attrs);
path.to_tokens(tokens);
brace_or_paren.surround(tokens, |tokens| {
match brace_or_paren {
BraceOrParen::Brace(_) => {
for (
StructOrEnumLiteralField {
attrs,
member,
mut colon_token,
expr,
},
comma,
) in fields.pairs().map(|v| v.into_tuple())
{
tokens.append_all(attrs);
if Some(member) != expr_to_member(expr).as_ref() {
colon_token = Some(<Token![:]>::default());
}
member.to_tokens(tokens);
colon_token.to_tokens(tokens);
expr.to_tokens(tokens);
comma.to_tokens(tokens);
}
}
BraceOrParen::Paren(_) => {
for (
StructOrEnumLiteralField {
attrs,
member: _,
colon_token: _,
expr,
},
comma,
) in fields.pairs().map(|v| v.into_tuple())
{
tokens.append_all(attrs);
expr.to_tokens(tokens);
comma.to_tokens(tokens);
}
}
}
if let Some(rest) = rest {
dot2_token.unwrap_or_default().to_tokens(tokens);
rest.to_tokens(tokens);
}
});
}
}
impl Visitor {
impl Visitor<'_> {
pub(crate) fn process_hdl_array(
&mut self,
hdl_attr: HdlAttr<Nothing>,
@ -356,100 +33,61 @@ impl Visitor {
};
parse_quote! {::fayalite::expr::ToExpr::to_expr(&#expr_repeat)}
}
pub(crate) fn process_struct_enum(
&mut self,
hdl_attr: HdlAttr<AggregateLiteralOptions>,
mut literal: StructOrEnumLiteral,
) -> Expr {
let span = hdl_attr.hdl.span;
if let Some(rest) = literal.rest.take() {
self.errors
.error(rest, "#[hdl] struct functional update syntax not supported");
}
let mut next_var = 0usize;
let mut new_var = || -> Ident {
let retval = format_ident!("__v{}", next_var, span = span);
next_var += 1;
retval
};
let infallible_var = new_var();
let retval_var = new_var();
let mut lets = vec![];
let mut build_steps = vec![];
let literal = literal.map_field_exprs(|expr| {
let field_var = new_var();
lets.push(quote_spanned! {span=>
let #field_var = ::fayalite::expr::ToExpr::to_expr(&#expr);
});
parse_quote! { #field_var }
});
let Ok(StructOrEnumPath { ty, variant }) =
StructOrEnumPath::new(&mut self.errors, literal.path.clone(), &hdl_attr.body)
else {
return parse_quote_spanned! {span=>
{}
};
};
for StructOrEnumLiteralField {
attrs: _,
member,
colon_token: _,
expr,
} in literal.fields.iter()
{
let field_fn = format_ident!("field_{}", member);
build_steps.push(quote_spanned! {span=>
let #retval_var = #retval_var.#field_fn(#expr);
});
}
let check_literal = literal.map_field_exprs(|expr| {
parse_quote_spanned! {span=>
::fayalite::expr::value_from_expr_type(#expr, #infallible_var)
}
});
let make_expr_fn = if let Some((_variant_path, variant_ident)) = &variant {
let variant_fn = format_ident!("variant_{}", variant_ident);
build_steps.push(quote_spanned! {span=>
let #retval_var = #retval_var.#variant_fn();
});
quote_spanned! {span=>
::fayalite::expr::make_enum_expr
}
} else {
build_steps.push(quote_spanned! {span=>
let #retval_var = #retval_var.build();
});
quote_spanned! {span=>
::fayalite::expr::make_bundle_expr
}
};
let variant_or_type =
variant.map_or_else(|| ty.clone(), |(variant_path, _variant_ident)| variant_path);
parse_quote_spanned! {span=>
{
#(#lets)*
#make_expr_fn::<#ty>(|#infallible_var| {
let #retval_var = #check_literal;
#[allow(unreachable_code)]
match #retval_var {
#variant_or_type { .. } => #retval_var,
#[allow(unreachable_patterns)]
_ => match #infallible_var {},
}
}, |#retval_var| {
#(#build_steps)*
#retval_var
})
}
}
}
pub(crate) fn process_hdl_struct(
&mut self,
hdl_attr: HdlAttr<AggregateLiteralOptions>,
hdl_attr: HdlAttr<Nothing>,
expr_struct: ExprStruct,
) -> Expr {
self.require_normal_module(&hdl_attr);
self.process_struct_enum(hdl_attr, expr_struct.into())
let name_span = expr_struct.path.segments.last().unwrap().ident.span();
let builder_ident = format_ident!("__builder", span = name_span);
let empty_builder = if expr_struct.qself.is_some()
|| expr_struct
.path
.segments
.iter()
.any(|seg| !seg.arguments.is_none())
{
let ty = TypePath {
qself: expr_struct.qself,
path: expr_struct.path,
};
let builder_ty = quote_spanned! {name_span=>
<#ty as ::fayalite::bundle::BundleType>::Builder
};
quote_spanned! {name_span=>
<#builder_ty as ::fayalite::__std::default::Default>::default()
}
} else {
let path = ExprPath {
attrs: vec![],
qself: expr_struct.qself,
path: expr_struct.path,
};
quote_spanned! {name_span=>
#path::__bundle_builder()
}
};
let field_calls = Vec::from_iter(expr_struct.fields.iter().map(
|FieldValue {
attrs: _,
member,
colon_token: _,
expr,
}| {
let field_fn = format_ident!("field_{}", member);
quote_spanned! {member.span()=>
let #builder_ident = #builder_ident.#field_fn(#expr);
}
},
));
parse_quote_spanned! {name_span=>
{
let #builder_ident = #empty_builder;
#(#field_calls)*
::fayalite::expr::ToExpr::to_expr(&#builder_ident)
}
}
}
pub(crate) fn process_hdl_tuple(
&mut self,
@ -461,80 +99,4 @@ impl Visitor {
::fayalite::expr::ToExpr::to_expr(&#expr_tuple)
}
}
pub(crate) fn process_hdl_path(
&mut self,
hdl_attr: HdlAttr<Nothing>,
expr_path: ExprPath,
) -> Expr {
self.require_normal_module(hdl_attr);
parse_quote_spanned! {expr_path.span()=>
::fayalite::expr::ToExpr::to_expr(&#expr_path)
}
}
pub(crate) fn process_hdl_call(
&mut self,
hdl_attr: HdlAttr<AggregateLiteralOptions>,
expr_call: ExprCall,
) -> Expr {
self.require_normal_module(&hdl_attr);
let ExprCall {
attrs: mut literal_attrs,
func,
paren_token,
args,
} = expr_call;
let mut path_expr = *func;
let path = loop {
break match path_expr {
Expr::Group(ExprGroup {
attrs,
group_token: _,
expr,
}) => {
literal_attrs.extend(attrs);
path_expr = *expr;
continue;
}
Expr::Path(ExprPath { attrs, qself, path }) => {
literal_attrs.extend(attrs);
TypePath { qself, path }
}
_ => {
self.errors.error(&path_expr, "missing tuple struct's name");
return parse_quote_spanned! {path_expr.span()=>
{}
};
}
};
};
let fields = args
.into_pairs()
.enumerate()
.map(|(index, p)| {
let (expr, comma) = p.into_tuple();
let mut index = Index::from(index);
index.span = hdl_attr.hdl.span;
Pair::new(
StructOrEnumLiteralField {
attrs: vec![],
member: Member::Unnamed(index),
colon_token: None,
expr,
},
comma,
)
})
.collect();
self.process_struct_enum(
hdl_attr,
StructOrEnumLiteral {
attrs: literal_attrs,
path,
brace_or_paren: BraceOrParen::Paren(paren_token),
fields,
dot2_token: None,
rest: None,
},
)
}
}

View file

@ -2,23 +2,20 @@
// See Notices.txt for copyright information
use crate::{
fold::impl_fold,
module::transform_body::{
expand_aggregate_literals::{AggregateLiteralOptions, StructOrEnumPath},
with_debug_clone_and_fold, Visitor,
},
module::transform_body::{with_debug_clone_and_fold, Visitor},
Errors, HdlAttr, PairsIterExt,
};
use proc_macro2::{Span, TokenStream};
use quote::{ToTokens, TokenStreamExt};
use quote::{format_ident, ToTokens, TokenStreamExt};
use syn::{
fold::{fold_arm, fold_expr_match, fold_pat, Fold},
parse::Nothing,
parse_quote_spanned,
punctuated::{Pair, Punctuated},
punctuated::Punctuated,
spanned::Spanned,
token::{Brace, Paren},
Arm, Attribute, Expr, ExprMatch, FieldPat, Ident, Index, Member, Pat, PatIdent, PatOr,
PatParen, PatPath, PatRest, PatStruct, PatTupleStruct, PatWild, Path, Token, TypePath,
Arm, Attribute, Expr, ExprMatch, FieldPat, Ident, Member, Pat, PatIdent, PatOr, PatParen,
PatPath, PatRest, PatStruct, PatTupleStruct, PatWild, Path, PathSegment, Token, TypePath,
};
with_debug_clone_and_fold! {
@ -81,7 +78,7 @@ impl ToTokens for MatchPatWild {
with_debug_clone_and_fold! {
struct MatchPatStructField<> {
member: Member,
field_name: Ident,
colon_token: Option<Token![:]>,
pat: MatchPatSimple,
}
@ -90,12 +87,19 @@ with_debug_clone_and_fold! {
impl ToTokens for MatchPatStructField {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self {
member,
field_name,
colon_token,
pat,
} = self;
member.to_tokens(tokens);
colon_token.to_tokens(tokens);
field_name.to_tokens(tokens);
if let (None, MatchPatSimple::Binding(MatchPatBinding { ident })) = (colon_token, pat) {
if field_name == ident {
return;
}
}
colon_token
.unwrap_or_else(|| Token![:](field_name.span()))
.to_tokens(tokens);
pat.to_tokens(tokens);
}
}
@ -108,8 +112,16 @@ impl MatchPatStructField {
colon_token,
pat,
} = field_pat;
let field_name = if let Member::Named(field_name) = member {
field_name
} else {
state
.errors
.error(&member, "field name must not be a number");
format_ident!("_{}", member)
};
Ok(Self {
member,
field_name,
colon_token,
pat: MatchPatSimple::parse(state, *pat)?,
})
@ -118,7 +130,7 @@ impl MatchPatStructField {
with_debug_clone_and_fold! {
struct MatchPatStruct<> {
resolved_path: Path,
path: Path,
brace_token: Brace,
fields: Punctuated<MatchPatStructField, Token![,]>,
rest: Option<Token![..]>,
@ -128,12 +140,12 @@ with_debug_clone_and_fold! {
impl ToTokens for MatchPatStruct {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self {
resolved_path,
path,
brace_token,
fields,
rest,
} = self;
resolved_path.to_tokens(tokens);
path.to_tokens(tokens);
brace_token.surround(tokens, |tokens| {
fields.to_tokens(tokens);
rest.to_tokens(tokens);
@ -141,6 +153,30 @@ impl ToTokens for MatchPatStruct {
}
}
with_debug_clone_and_fold! {
struct MatchPatEnumVariant<> {
variant_path: Path,
enum_path: Path,
variant_name: Ident,
field: Option<(Paren, MatchPatSimple)>,
}
}
impl ToTokens for MatchPatEnumVariant {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self {
variant_path,
enum_path: _,
variant_name: _,
field,
} = self;
variant_path.to_tokens(tokens);
if let Some((paren_token, field)) = field {
paren_token.surround(tokens, |tokens| field.to_tokens(tokens));
}
}
}
#[derive(Debug, Clone)]
enum MatchPatSimple {
Paren(MatchPatParen<MatchPatSimple>),
@ -169,21 +205,70 @@ impl ToTokens for MatchPatSimple {
}
}
fn is_pat_ident_a_struct_or_enum_name(ident: &Ident) -> bool {
ident
.to_string()
.starts_with(|ch: char| ch.is_ascii_uppercase())
struct EnumPath {
variant_path: Path,
enum_path: Path,
variant_name: Ident,
}
fn parse_enum_path(variant_path: TypePath) -> Result<EnumPath, TypePath> {
let TypePath {
qself: None,
path: variant_path,
} = variant_path
else {
return Err(variant_path);
};
if variant_path.is_ident("HdlNone") || variant_path.is_ident("HdlSome") {
let ident = variant_path.get_ident().unwrap();
return Ok(EnumPath {
enum_path: parse_quote_spanned! {ident.span()=>
::fayalite::enum_::HdlOption::<_>
},
variant_name: ident.clone(),
variant_path,
});
}
if variant_path.segments.len() < 2 {
return Err(TypePath {
qself: None,
path: variant_path,
});
}
let mut enum_path = variant_path.clone();
let PathSegment {
ident: variant_name,
arguments,
} = enum_path.segments.pop().unwrap().into_value();
if !arguments.is_none() {
return Err(TypePath {
qself: None,
path: variant_path,
});
}
enum_path.segments.pop_punct();
Ok(EnumPath {
variant_path,
enum_path,
variant_name,
})
}
fn parse_enum_ident(ident: Ident) -> Result<EnumPath, Ident> {
parse_enum_path(TypePath {
qself: None,
path: ident.into(),
})
.map_err(|p| p.path.segments.into_iter().next().unwrap().ident)
}
trait ParseMatchPat: Sized {
fn simple(v: MatchPatSimple) -> Self;
fn or(v: MatchPatOr<Self>) -> Self;
fn paren(v: MatchPatParen<Self>) -> Self;
fn struct_(
state: &mut HdlMatchParseState<'_>,
v: MatchPatStruct,
struct_error_spanned: &dyn ToTokens,
) -> Result<Self, ()>;
fn struct_(state: &mut HdlMatchParseState<'_>, v: MatchPatStruct) -> Result<Self, ()>;
fn enum_variant(state: &mut HdlMatchParseState<'_>, v: MatchPatEnumVariant)
-> Result<Self, ()>;
fn parse(state: &mut HdlMatchParseState<'_>, pat: Pat) -> Result<Self, ()> {
match pat {
Pat::Ident(PatIdent {
@ -208,26 +293,23 @@ trait ParseMatchPat: Sized {
.errors
.error(at_token, "@ not allowed in #[hdl] patterns");
}
if is_pat_ident_a_struct_or_enum_name(&ident) {
let ident_span = ident.span();
let resolved_path = state.resolve_enum_struct_path(TypePath {
qself: None,
path: ident.clone().into(),
})?;
Self::struct_(
match parse_enum_ident(ident) {
Ok(EnumPath {
variant_path,
enum_path,
variant_name,
}) => Self::enum_variant(
state,
MatchPatStruct {
resolved_path,
brace_token: Brace(ident_span),
fields: Punctuated::new(),
rest: None,
MatchPatEnumVariant {
variant_path,
enum_path,
variant_name,
field: None,
},
&ident,
)
} else {
Ok(Self::simple(MatchPatSimple::Binding(MatchPatBinding {
),
Err(ident) => Ok(Self::simple(MatchPatSimple::Binding(MatchPatBinding {
ident,
})))
}))),
}
}
Pat::Or(PatOr {
@ -254,18 +336,21 @@ trait ParseMatchPat: Sized {
qself,
path,
}) => {
let path = TypePath { qself, path };
let path_span = path.span();
let resolved_path = state.resolve_enum_struct_path(path.clone())?;
Self::struct_(
let EnumPath {
variant_path,
enum_path,
variant_name,
} = parse_enum_path(TypePath { qself, path }).map_err(|path| {
state.errors.error(path, "unsupported enum variant path");
})?;
Self::enum_variant(
state,
MatchPatStruct {
resolved_path,
brace_token: Brace(path_span),
fields: Punctuated::new(),
rest: None,
MatchPatEnumVariant {
variant_path,
enum_path,
variant_name,
field: None,
},
&path,
)
}
Pat::Struct(PatStruct {
@ -282,12 +367,16 @@ trait ParseMatchPat: Sized {
MatchPatStructField::parse(state, field_pat).ok()
})
.collect();
let path = TypePath { qself, path };
let resolved_path = state.resolve_enum_struct_path(path.clone())?;
if qself.is_some() {
state
.errors
.error(TypePath { qself, path }, "unsupported struct path");
return Err(());
}
Self::struct_(
state,
MatchPatStruct {
resolved_path,
path,
brace_token,
fields,
rest: rest.map(
@ -297,7 +386,6 @@ trait ParseMatchPat: Sized {
}| dot2_token,
),
},
&path,
)
}
Pat::TupleStruct(PatTupleStruct {
@ -307,45 +395,44 @@ trait ParseMatchPat: Sized {
paren_token,
mut elems,
}) => {
let rest = if let Some(&Pat::Rest(PatRest {
attrs: _,
dot2_token,
})) = elems.last()
{
elems.pop();
Some(dot2_token)
} else {
None
};
let fields = elems
.into_pairs()
.enumerate()
.filter_map(|(index, pair)| {
let (pat, punct) = pair.into_tuple();
let pat = MatchPatSimple::parse(state, pat).ok()?;
let mut index = Index::from(index);
index.span = state.span;
let field = MatchPatStructField {
member: index.into(),
colon_token: Some(Token![:](state.span)),
pat,
};
Some(Pair::new(field, punct))
let EnumPath {
variant_path,
enum_path,
variant_name,
} = parse_enum_path(TypePath { qself, path }).map_err(|path| {
state.errors.error(path, "unsupported enum variant path");
})?;
if elems.is_empty() {
let mut tokens = TokenStream::new();
paren_token.surround(&mut tokens, |_| {});
state.errors.error(
tokens,
"field-less enum variants must not be matched using parenthesis",
);
}
if elems.len() != 1 {
state.errors.error(
variant_path,
"enum variant pattern must have exactly one field",
);
return Err(());
}
let field = elems.pop().unwrap().into_value();
let field = if let Pat::Rest(rest) = field {
MatchPatSimple::Wild(MatchPatWild {
underscore_token: Token![_](rest.dot2_token.span()),
})
.collect();
let path = TypePath { qself, path };
let resolved_path = state.resolve_enum_struct_path(path.clone())?;
Self::struct_(
} else {
MatchPatSimple::parse(state, field)?
};
Self::enum_variant(
state,
MatchPatStruct {
resolved_path,
brace_token: Brace {
span: paren_token.span,
},
fields,
rest,
MatchPatEnumVariant {
variant_path,
enum_path,
variant_name,
field: Some((paren_token, field)),
},
&path,
)
}
Pat::Rest(_) => {
@ -387,14 +474,21 @@ impl ParseMatchPat for MatchPatSimple {
Self::Paren(v)
}
fn struct_(
fn struct_(state: &mut HdlMatchParseState<'_>, v: MatchPatStruct) -> Result<Self, ()> {
state.errors.error(
v.path,
"matching structs is not yet implemented inside structs/enums in #[hdl] patterns",
);
Err(())
}
fn enum_variant(
state: &mut HdlMatchParseState<'_>,
_v: MatchPatStruct,
struct_error_spanned: &dyn ToTokens,
v: MatchPatEnumVariant,
) -> Result<Self, ()> {
state.errors.error(
struct_error_spanned,
"not yet implemented inside structs/enums in #[hdl] patterns",
v.variant_path,
"matching enum variants is not yet implemented inside structs/enums in #[hdl] patterns",
);
Err(())
}
@ -406,6 +500,7 @@ enum MatchPat {
Or(MatchPatOr<MatchPat>),
Paren(MatchPatParen<MatchPat>),
Struct(MatchPatStruct),
EnumVariant(MatchPatEnumVariant),
}
impl_fold! {
@ -414,6 +509,7 @@ impl_fold! {
Or(MatchPatOr<MatchPat>),
Paren(MatchPatParen<MatchPat>),
Struct(MatchPatStruct),
EnumVariant(MatchPatEnumVariant),
}
}
@ -430,13 +526,16 @@ impl ParseMatchPat for MatchPat {
Self::Paren(v)
}
fn struct_(
_state: &mut HdlMatchParseState<'_>,
v: MatchPatStruct,
_struct_error_spanned: &dyn ToTokens,
) -> Result<Self, ()> {
fn struct_(_state: &mut HdlMatchParseState<'_>, v: MatchPatStruct) -> Result<Self, ()> {
Ok(Self::Struct(v))
}
fn enum_variant(
_state: &mut HdlMatchParseState<'_>,
v: MatchPatEnumVariant,
) -> Result<Self, ()> {
Ok(Self::EnumVariant(v))
}
}
impl ToTokens for MatchPat {
@ -446,6 +545,7 @@ impl ToTokens for MatchPat {
Self::Or(v) => v.to_tokens(tokens),
Self::Paren(v) => v.to_tokens(tokens),
Self::Struct(v) => v.to_tokens(tokens),
Self::EnumVariant(v) => v.to_tokens(tokens),
}
}
}
@ -511,23 +611,58 @@ impl Fold for RewriteAsCheckMatch {
i.colon_token = Some(Token![:](i.member.span()));
i
}
fn fold_pat(&mut self, i: Pat) -> Pat {
match i {
Pat::Ident(PatIdent {
attrs,
by_ref,
mutability,
ident,
subpat: None,
}) if is_pat_ident_a_struct_or_enum_name(&ident) => {
parse_quote_spanned! {ident.span()=>
#(#attrs)*
#by_ref
#mutability
#ident {}
fn fold_pat(&mut self, pat: Pat) -> Pat {
match pat {
Pat::Ident(mut pat_ident) => match parse_enum_ident(pat_ident.ident) {
Ok(EnumPath {
variant_path: _,
enum_path,
variant_name,
}) => parse_quote_spanned! {self.span=>
__MatchTy::<#enum_path>::#variant_name {}
},
Err(ident) => {
pat_ident.ident = ident;
Pat::Ident(self.fold_pat_ident(pat_ident))
}
}
_ => fold_pat(self, i),
},
Pat::TupleStruct(PatTupleStruct {
attrs,
qself,
path,
paren_token,
elems,
}) => match parse_enum_path(TypePath { qself, path }) {
Ok(EnumPath {
variant_path: _,
enum_path,
variant_name,
}) => {
let path = parse_quote_spanned! {self.span=>
__MatchTy::<#enum_path>::#variant_name
};
let elems = Punctuated::from_iter(
elems.into_pairs().map_pair_value(|p| fold_pat(self, p)),
);
Pat::TupleStruct(PatTupleStruct {
attrs,
qself: None,
path,
paren_token,
elems,
})
}
Err(TypePath { qself, path }) => {
Pat::TupleStruct(self.fold_pat_tuple_struct(PatTupleStruct {
attrs,
qself,
path,
paren_token,
elems,
}))
}
},
_ => fold_pat(self, pat),
}
}
fn fold_pat_ident(&mut self, mut i: PatIdent) -> PatIdent {
@ -556,26 +691,9 @@ impl Fold for RewriteAsCheckMatch {
struct HdlMatchParseState<'a> {
errors: &'a mut Errors,
span: Span,
}
impl HdlMatchParseState<'_> {
fn resolve_enum_struct_path(&mut self, path: TypePath) -> Result<Path, ()> {
let StructOrEnumPath { ty, variant } =
StructOrEnumPath::new(self.errors, path, &AggregateLiteralOptions::default())?;
Ok(if let Some((_variant_path, variant_name)) = variant {
parse_quote_spanned! {self.span=>
__MatchTy::<#ty>::#variant_name
}
} else {
parse_quote_spanned! {self.span=>
__MatchTy::<#ty>
}
})
}
}
impl Visitor {
impl Visitor<'_> {
pub(crate) fn process_hdl_match(
&mut self,
_hdl_attr: HdlAttr<Nothing>,
@ -593,22 +711,20 @@ impl Visitor {
self.require_normal_module(match_token);
let mut state = HdlMatchParseState {
errors: &mut self.errors,
span,
};
let arms = Vec::from_iter(
arms.into_iter()
.filter_map(|arm| MatchArm::parse(&mut state, arm).ok()),
);
parse_quote_spanned! {span=>
let expr = quote::quote_spanned! {span=>
{
type __MatchTy<V> =
<<V as ::fayalite::expr::ToExpr>::Type as ::fayalite::ty::Type>::MatchVariant;
type __MatchTy<T> = <T as ::fayalite::ty::Type>::MatchVariant;
let __match_expr = ::fayalite::expr::ToExpr::to_expr(&(#expr));
::fayalite::expr::check_match_expr(__match_expr, |__match_value, __infallible| {
#[allow(unused_variables)]
#check_match
});
for __match_variant in m.match_(__match_expr) {
for __match_variant in ::fayalite::module::match_(__match_expr) {
let (__match_variant, __scope) =
::fayalite::ty::MatchVariantAndInactiveScope::match_activate_scope(
__match_variant,
@ -618,6 +734,8 @@ impl Visitor {
}
}
}
}
};
println!("{}", expr);
syn::parse2(expr).unwrap()
}
}