forked from libre-chip/fayalite
initial public commit
This commit is contained in:
commit
0b958e7852
56 changed files with 30235 additions and 0 deletions
1566
crates/fayalite-proc-macros-impl/src/module/transform_body.rs
Normal file
1566
crates/fayalite-proc-macros-impl/src/module/transform_body.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,530 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use crate::{module::transform_body::Visitor, options, Errors, HdlAttr};
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::{format_ident, quote_spanned, ToTokens, TokenStreamExt};
|
||||
use syn::{
|
||||
parse::Nothing,
|
||||
parse_quote, parse_quote_spanned,
|
||||
punctuated::{Pair, Punctuated},
|
||||
spanned::Spanned,
|
||||
token::{Brace, Paren},
|
||||
Attribute, Expr, ExprArray, ExprCall, ExprGroup, ExprPath, ExprStruct, ExprTuple, FieldValue,
|
||||
Ident, Index, Member, Path, PathArguments, PathSegment, Token, 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,
|
||||
mut f: impl FnMut(StructOrEnumLiteralField) -> StructOrEnumLiteralField,
|
||||
) -> Self {
|
||||
let Self {
|
||||
attrs,
|
||||
path,
|
||||
brace_or_paren,
|
||||
fields,
|
||||
dot2_token,
|
||||
rest,
|
||||
} = self;
|
||||
let fields = Punctuated::from_iter(fields.into_pairs().map(|p| {
|
||||
let (field, comma) = p.into_tuple();
|
||||
Pair::new(f(field), comma)
|
||||
}));
|
||||
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: Punctuated::from_iter(fields.into_pairs().map(|v| {
|
||||
let (
|
||||
FieldValue {
|
||||
attrs,
|
||||
member,
|
||||
colon_token,
|
||||
expr,
|
||||
},
|
||||
comma,
|
||||
) = v.into_tuple();
|
||||
Pair::new(
|
||||
StructOrEnumLiteralField {
|
||||
attrs,
|
||||
member,
|
||||
colon_token,
|
||||
expr,
|
||||
},
|
||||
comma,
|
||||
)
|
||||
})),
|
||||
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 {
|
||||
pub(crate) fn process_hdl_array(
|
||||
&mut self,
|
||||
hdl_attr: HdlAttr<Nothing>,
|
||||
mut expr_array: ExprArray,
|
||||
) -> Expr {
|
||||
self.require_normal_module(hdl_attr);
|
||||
for elem in &mut expr_array.elems {
|
||||
*elem = parse_quote_spanned! {elem.span()=>
|
||||
::fayalite::expr::ToExpr::to_expr(&(#elem))
|
||||
};
|
||||
}
|
||||
parse_quote! {::fayalite::expr::ToExpr::to_expr(&#expr_array)}
|
||||
}
|
||||
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;
|
||||
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>,
|
||||
expr_struct: ExprStruct,
|
||||
) -> Expr {
|
||||
self.require_normal_module(&hdl_attr);
|
||||
self.process_struct_enum(hdl_attr, expr_struct.into())
|
||||
}
|
||||
pub(crate) fn process_hdl_tuple(
|
||||
&mut self,
|
||||
hdl_attr: HdlAttr<Nothing>,
|
||||
expr_tuple: ExprTuple,
|
||||
) -> Expr {
|
||||
self.require_normal_module(hdl_attr);
|
||||
parse_quote_spanned! {expr_tuple.span()=>
|
||||
::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 = Punctuated::from_iter(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,
|
||||
)
|
||||
}));
|
||||
self.process_struct_enum(
|
||||
hdl_attr,
|
||||
StructOrEnumLiteral {
|
||||
attrs: literal_attrs,
|
||||
path,
|
||||
brace_or_paren: BraceOrParen::Paren(paren_token),
|
||||
fields,
|
||||
dot2_token: None,
|
||||
rest: None,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,625 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// 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,
|
||||
},
|
||||
Errors, HdlAttr,
|
||||
};
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::{ToTokens, TokenStreamExt};
|
||||
use syn::{
|
||||
fold::{fold_arm, fold_expr_match, fold_pat, Fold},
|
||||
parse::Nothing,
|
||||
parse_quote_spanned,
|
||||
punctuated::{Pair, 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,
|
||||
};
|
||||
|
||||
with_debug_clone_and_fold! {
|
||||
struct MatchPatBinding<> {
|
||||
ident: Ident,
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for MatchPatBinding {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let Self { ident } = self;
|
||||
ident.to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
|
||||
with_debug_clone_and_fold! {
|
||||
struct MatchPatParen<P> {
|
||||
paren_token: Paren,
|
||||
pat: Box<P>,
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: ToTokens> ToTokens for MatchPatParen<P> {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let Self { paren_token, pat } = self;
|
||||
paren_token.surround(tokens, |tokens| pat.to_tokens(tokens));
|
||||
}
|
||||
}
|
||||
|
||||
with_debug_clone_and_fold! {
|
||||
struct MatchPatOr<P> {
|
||||
leading_vert: Option<Token![|]>,
|
||||
cases: Punctuated<P, Token![|]>,
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: ToTokens> ToTokens for MatchPatOr<P> {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let Self {
|
||||
leading_vert,
|
||||
cases,
|
||||
} = self;
|
||||
leading_vert.to_tokens(tokens);
|
||||
cases.to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
|
||||
with_debug_clone_and_fold! {
|
||||
struct MatchPatWild<> {
|
||||
underscore_token: Token![_],
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for MatchPatWild {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let Self { underscore_token } = self;
|
||||
underscore_token.to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
|
||||
with_debug_clone_and_fold! {
|
||||
struct MatchPatStructField<> {
|
||||
member: Member,
|
||||
colon_token: Option<Token![:]>,
|
||||
pat: MatchPatSimple,
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for MatchPatStructField {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let Self {
|
||||
member,
|
||||
colon_token,
|
||||
pat,
|
||||
} = self;
|
||||
member.to_tokens(tokens);
|
||||
colon_token.to_tokens(tokens);
|
||||
pat.to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
|
||||
impl MatchPatStructField {
|
||||
fn parse(state: &mut HdlMatchParseState<'_>, field_pat: FieldPat) -> Result<Self, ()> {
|
||||
let FieldPat {
|
||||
attrs: _,
|
||||
member,
|
||||
colon_token,
|
||||
pat,
|
||||
} = field_pat;
|
||||
Ok(Self {
|
||||
member,
|
||||
colon_token,
|
||||
pat: MatchPatSimple::parse(state, *pat)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
with_debug_clone_and_fold! {
|
||||
struct MatchPatStruct<> {
|
||||
resolved_path: Path,
|
||||
brace_token: Brace,
|
||||
fields: Punctuated<MatchPatStructField, Token![,]>,
|
||||
rest: Option<Token![..]>,
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for MatchPatStruct {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let Self {
|
||||
resolved_path,
|
||||
brace_token,
|
||||
fields,
|
||||
rest,
|
||||
} = self;
|
||||
resolved_path.to_tokens(tokens);
|
||||
brace_token.surround(tokens, |tokens| {
|
||||
fields.to_tokens(tokens);
|
||||
rest.to_tokens(tokens);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum MatchPatSimple {
|
||||
Paren(MatchPatParen<MatchPatSimple>),
|
||||
Or(MatchPatOr<MatchPatSimple>),
|
||||
Binding(MatchPatBinding),
|
||||
Wild(MatchPatWild),
|
||||
}
|
||||
|
||||
impl_fold! {
|
||||
enum MatchPatSimple<> {
|
||||
Paren(MatchPatParen<MatchPatSimple>),
|
||||
Or(MatchPatOr<MatchPatSimple>),
|
||||
Binding(MatchPatBinding),
|
||||
Wild(MatchPatWild),
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for MatchPatSimple {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
match self {
|
||||
Self::Or(v) => v.to_tokens(tokens),
|
||||
Self::Paren(v) => v.to_tokens(tokens),
|
||||
Self::Binding(v) => v.to_tokens(tokens),
|
||||
Self::Wild(v) => v.to_tokens(tokens),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_pat_ident_a_struct_or_enum_name(ident: &Ident) -> bool {
|
||||
ident
|
||||
.to_string()
|
||||
.starts_with(|ch: char| ch.is_ascii_uppercase())
|
||||
}
|
||||
|
||||
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 parse(state: &mut HdlMatchParseState<'_>, pat: Pat) -> Result<Self, ()> {
|
||||
match pat {
|
||||
Pat::Ident(PatIdent {
|
||||
attrs: _,
|
||||
by_ref,
|
||||
mutability,
|
||||
ident,
|
||||
subpat,
|
||||
}) => {
|
||||
if let Some(by_ref) = by_ref {
|
||||
state
|
||||
.errors
|
||||
.error(by_ref, "ref not allowed in #[hdl] patterns");
|
||||
}
|
||||
if let Some(mutability) = mutability {
|
||||
state
|
||||
.errors
|
||||
.error(mutability, "mut not allowed in #[hdl] patterns");
|
||||
}
|
||||
if let Some((at_token, _)) = subpat {
|
||||
state
|
||||
.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_(
|
||||
state,
|
||||
MatchPatStruct {
|
||||
resolved_path,
|
||||
brace_token: Brace(ident_span),
|
||||
fields: Punctuated::new(),
|
||||
rest: None,
|
||||
},
|
||||
&ident,
|
||||
)
|
||||
} else {
|
||||
Ok(Self::simple(MatchPatSimple::Binding(MatchPatBinding {
|
||||
ident,
|
||||
})))
|
||||
}
|
||||
}
|
||||
Pat::Or(PatOr {
|
||||
attrs: _,
|
||||
leading_vert,
|
||||
cases,
|
||||
}) => Ok(Self::or(MatchPatOr {
|
||||
leading_vert,
|
||||
cases: cases
|
||||
.into_pairs()
|
||||
.filter_map(|pair| {
|
||||
let (pat, punct) = pair.into_tuple();
|
||||
let pat = Self::parse(state, pat).ok()?;
|
||||
Some(Pair::new(pat, punct))
|
||||
})
|
||||
.collect(),
|
||||
})),
|
||||
Pat::Paren(PatParen {
|
||||
attrs: _,
|
||||
paren_token,
|
||||
pat,
|
||||
}) => Ok(Self::paren(MatchPatParen {
|
||||
paren_token,
|
||||
pat: Box::new(Self::parse(state, *pat)?),
|
||||
})),
|
||||
Pat::Path(PatPath {
|
||||
attrs: _,
|
||||
qself,
|
||||
path,
|
||||
}) => {
|
||||
let path = TypePath { qself, path };
|
||||
let path_span = path.span();
|
||||
let resolved_path = state.resolve_enum_struct_path(path.clone())?;
|
||||
Self::struct_(
|
||||
state,
|
||||
MatchPatStruct {
|
||||
resolved_path,
|
||||
brace_token: Brace(path_span),
|
||||
fields: Punctuated::new(),
|
||||
rest: None,
|
||||
},
|
||||
&path,
|
||||
)
|
||||
}
|
||||
Pat::Struct(PatStruct {
|
||||
attrs: _,
|
||||
qself,
|
||||
path,
|
||||
brace_token,
|
||||
fields,
|
||||
rest,
|
||||
}) => {
|
||||
let fields = fields
|
||||
.into_pairs()
|
||||
.filter_map(|pair| {
|
||||
let (field_pat, punct) = pair.into_tuple();
|
||||
let field_pat = MatchPatStructField::parse(state, field_pat).ok()?;
|
||||
Some(Pair::new(field_pat, punct))
|
||||
})
|
||||
.collect();
|
||||
let path = TypePath { qself, path };
|
||||
let resolved_path = state.resolve_enum_struct_path(path.clone())?;
|
||||
Self::struct_(
|
||||
state,
|
||||
MatchPatStruct {
|
||||
resolved_path,
|
||||
brace_token,
|
||||
fields,
|
||||
rest: rest.map(
|
||||
|PatRest {
|
||||
attrs: _,
|
||||
dot2_token,
|
||||
}| dot2_token,
|
||||
),
|
||||
},
|
||||
&path,
|
||||
)
|
||||
}
|
||||
Pat::TupleStruct(PatTupleStruct {
|
||||
attrs: _,
|
||||
qself,
|
||||
path,
|
||||
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),
|
||||
pat,
|
||||
};
|
||||
Some(Pair::new(field, punct))
|
||||
})
|
||||
.collect();
|
||||
let path = TypePath { qself, path };
|
||||
let resolved_path = state.resolve_enum_struct_path(path.clone())?;
|
||||
Self::struct_(
|
||||
state,
|
||||
MatchPatStruct {
|
||||
resolved_path,
|
||||
brace_token: Brace {
|
||||
span: paren_token.span,
|
||||
},
|
||||
fields,
|
||||
rest,
|
||||
},
|
||||
&path,
|
||||
)
|
||||
}
|
||||
Pat::Rest(_) => {
|
||||
state
|
||||
.errors
|
||||
.error(pat, "not allowed here in #[hdl] patterns");
|
||||
Err(())
|
||||
}
|
||||
Pat::Wild(PatWild {
|
||||
attrs: _,
|
||||
underscore_token,
|
||||
}) => Ok(Self::simple(MatchPatSimple::Wild(MatchPatWild {
|
||||
underscore_token,
|
||||
}))),
|
||||
Pat::Tuple(_) | Pat::Slice(_) | Pat::Const(_) | Pat::Lit(_) | Pat::Range(_) => {
|
||||
state
|
||||
.errors
|
||||
.error(pat, "not yet implemented in #[hdl] patterns");
|
||||
Err(())
|
||||
}
|
||||
_ => {
|
||||
state.errors.error(pat, "not allowed in #[hdl] patterns");
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ParseMatchPat for MatchPatSimple {
|
||||
fn simple(v: MatchPatSimple) -> Self {
|
||||
v
|
||||
}
|
||||
|
||||
fn or(v: MatchPatOr<Self>) -> Self {
|
||||
Self::Or(v)
|
||||
}
|
||||
|
||||
fn paren(v: MatchPatParen<Self>) -> Self {
|
||||
Self::Paren(v)
|
||||
}
|
||||
|
||||
fn struct_(
|
||||
state: &mut HdlMatchParseState<'_>,
|
||||
_v: MatchPatStruct,
|
||||
struct_error_spanned: &dyn ToTokens,
|
||||
) -> Result<Self, ()> {
|
||||
state.errors.error(
|
||||
struct_error_spanned,
|
||||
"not yet implemented inside structs/enums in #[hdl] patterns",
|
||||
);
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum MatchPat {
|
||||
Simple(MatchPatSimple),
|
||||
Or(MatchPatOr<MatchPat>),
|
||||
Paren(MatchPatParen<MatchPat>),
|
||||
Struct(MatchPatStruct),
|
||||
}
|
||||
|
||||
impl_fold! {
|
||||
enum MatchPat<> {
|
||||
Simple(MatchPatSimple),
|
||||
Or(MatchPatOr<MatchPat>),
|
||||
Paren(MatchPatParen<MatchPat>),
|
||||
Struct(MatchPatStruct),
|
||||
}
|
||||
}
|
||||
|
||||
impl ParseMatchPat for MatchPat {
|
||||
fn simple(v: MatchPatSimple) -> Self {
|
||||
Self::Simple(v)
|
||||
}
|
||||
|
||||
fn or(v: MatchPatOr<Self>) -> Self {
|
||||
Self::Or(v)
|
||||
}
|
||||
|
||||
fn paren(v: MatchPatParen<Self>) -> Self {
|
||||
Self::Paren(v)
|
||||
}
|
||||
|
||||
fn struct_(
|
||||
_state: &mut HdlMatchParseState<'_>,
|
||||
v: MatchPatStruct,
|
||||
_struct_error_spanned: &dyn ToTokens,
|
||||
) -> Result<Self, ()> {
|
||||
Ok(Self::Struct(v))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for MatchPat {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
match self {
|
||||
Self::Simple(v) => v.to_tokens(tokens),
|
||||
Self::Or(v) => v.to_tokens(tokens),
|
||||
Self::Paren(v) => v.to_tokens(tokens),
|
||||
Self::Struct(v) => v.to_tokens(tokens),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
with_debug_clone_and_fold! {
|
||||
struct MatchArm<> {
|
||||
attrs: Vec<Attribute>,
|
||||
pat: MatchPat,
|
||||
fat_arrow_token: Token![=>],
|
||||
body: Box<Expr>,
|
||||
comma: Option<Token![,]>,
|
||||
}
|
||||
}
|
||||
|
||||
impl MatchArm {
|
||||
fn parse(state: &mut HdlMatchParseState<'_>, arm: Arm) -> Result<Self, ()> {
|
||||
let Arm {
|
||||
attrs,
|
||||
pat,
|
||||
guard,
|
||||
fat_arrow_token,
|
||||
body,
|
||||
comma,
|
||||
} = arm;
|
||||
if let Some((if_, _)) = guard {
|
||||
state
|
||||
.errors
|
||||
.error(if_, "#[hdl] match arm if clauses are not implemented");
|
||||
}
|
||||
Ok(Self {
|
||||
attrs,
|
||||
pat: MatchPat::parse(state, pat)?,
|
||||
fat_arrow_token,
|
||||
body,
|
||||
comma,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for MatchArm {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let Self {
|
||||
attrs,
|
||||
pat,
|
||||
fat_arrow_token,
|
||||
body,
|
||||
comma,
|
||||
} = self;
|
||||
tokens.append_all(attrs);
|
||||
pat.to_tokens(tokens);
|
||||
fat_arrow_token.to_tokens(tokens);
|
||||
body.to_tokens(tokens);
|
||||
comma.to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
|
||||
struct RewriteAsCheckMatch {
|
||||
span: Span,
|
||||
}
|
||||
|
||||
impl Fold for RewriteAsCheckMatch {
|
||||
fn fold_field_pat(&mut self, mut i: FieldPat) -> FieldPat {
|
||||
i.colon_token = Some(Token));
|
||||
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 {}
|
||||
}
|
||||
}
|
||||
_ => fold_pat(self, i),
|
||||
}
|
||||
}
|
||||
fn fold_pat_ident(&mut self, mut i: PatIdent) -> PatIdent {
|
||||
i.by_ref = Some(Token));
|
||||
i.mutability = None;
|
||||
i
|
||||
}
|
||||
fn fold_arm(&mut self, mut i: Arm) -> Arm {
|
||||
i.body = parse_quote_spanned! {self.span=>
|
||||
match __infallible {}
|
||||
};
|
||||
i.comma.get_or_insert_with(|| Token);
|
||||
fold_arm(self, i)
|
||||
}
|
||||
fn fold_expr_match(&mut self, mut i: ExprMatch) -> ExprMatch {
|
||||
i.expr = parse_quote_spanned! {self.span=>
|
||||
__match_value
|
||||
};
|
||||
fold_expr_match(self, i)
|
||||
}
|
||||
fn fold_expr(&mut self, i: Expr) -> Expr {
|
||||
// don't recurse into expressions
|
||||
i
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
pub(crate) fn process_hdl_match(
|
||||
&mut self,
|
||||
_hdl_attr: HdlAttr<Nothing>,
|
||||
expr_match: ExprMatch,
|
||||
) -> Expr {
|
||||
let span = expr_match.match_token.span();
|
||||
let check_match = RewriteAsCheckMatch { span }.fold_expr_match(expr_match.clone());
|
||||
let ExprMatch {
|
||||
attrs: _,
|
||||
match_token,
|
||||
expr,
|
||||
brace_token: _,
|
||||
arms,
|
||||
} = expr_match;
|
||||
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=>
|
||||
{
|
||||
type __MatchTy<V> = <<V as ::fayalite::expr::ToExpr>::Type 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) {
|
||||
let (__match_variant, __scope) = ::fayalite::ty::MatchVariantAndInactiveScope::match_activate_scope(__match_variant);
|
||||
#match_token __match_variant {
|
||||
#(#arms)*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue