use drone_macros_core::unkeywordize;
use inflector::Inflector;
use proc_macro::TokenStream;
use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::{format_ident, quote};
use std::collections::HashSet;
use syn::{
braced,
parse::{Parse, ParseStream, Result},
parse_macro_input, Attribute, Ident, LitInt, LitStr, Token, Visibility,
};
struct Input {
variants: Vec<Variant>,
}
struct Variant {
attrs: Vec<Attribute>,
vis: Visibility,
block: Ident,
ident: Ident,
address: LitInt,
size: u8,
reset: LitInt,
traits: Vec<Ident>,
fields: Vec<Field>,
}
struct Field {
attrs: Vec<Attribute>,
ident: Ident,
offset: LitInt,
width: LitInt,
traits: Vec<Ident>,
}
impl Parse for Input {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let mut variants = Vec::new();
while !input.is_empty() {
variants.push(input.parse()?);
if !input.is_empty() {
input.parse::<Token![;]>()?;
}
}
Ok(Self { variants })
}
}
impl Parse for Variant {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let attrs = input.call(Attribute::parse_outer)?;
let vis = input.parse()?;
let block = input.parse()?;
let ident = input.parse()?;
input.parse::<Token![=>]>()?;
let input2;
braced!(input2 in input);
let mut address = None;
let mut size = None;
let mut reset = None;
let mut traits = Vec::new();
let mut fields = Vec::new();
while !input2.is_empty() {
let ident = input2.parse::<Ident>()?;
input2.parse::<Token![=>]>()?;
if ident == "address" {
if address.is_none() {
address = Some(input2.parse()?);
} else {
return Err(input2.error("multiple `address` specifications"));
}
} else if ident == "size" {
if size.is_none() {
size = Some(input2.parse::<LitInt>()?.base10_parse()?);
} else {
return Err(input2.error("multiple `size` specifications"));
}
} else if ident == "reset" {
if reset.is_none() {
reset = Some(input2.parse()?);
} else {
return Err(input2.error("multiple `reset` specifications"));
}
} else if ident == "traits" {
traits.extend(parse_traits(&input2)?);
} else if ident == "fields" {
fields.extend(Field::parse_list(&input2)?);
} else {
return Err(input2.error(format!("unknown key: `{}`", ident)));
}
if !input2.is_empty() {
input2.parse::<Token![;]>()?;
}
}
Ok(Self {
attrs,
vis,
block,
ident,
address: address.ok_or_else(|| input2.error("missing `address` specification"))?,
size: size.ok_or_else(|| input2.error("missing `size` specification"))?,
reset: reset.ok_or_else(|| input2.error("missing `reset` specification"))?,
traits,
fields,
})
}
}
impl Field {
fn parse_list(input: ParseStream<'_>) -> Result<Vec<Self>> {
let mut fields = Vec::new();
let input2;
braced!(input2 in input);
while !input2.is_empty() {
fields.push(input2.parse()?);
if !input2.is_empty() {
input2.parse::<Token![;]>()?;
}
}
Ok(fields)
}
}
impl Parse for Field {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let attrs = input.call(Attribute::parse_outer)?;
let ident = input.parse()?;
input.parse::<Token![=>]>()?;
let input2;
braced!(input2 in input);
let mut offset = None;
let mut width = None;
let mut traits = Vec::new();
while !input2.is_empty() {
let ident = input2.parse::<Ident>()?;
input2.parse::<Token![=>]>()?;
if ident == "offset" {
if offset.is_none() {
offset = Some(input2.parse()?);
} else {
return Err(input2.error("multiple `offset` specifications"));
}
} else if ident == "width" {
if width.is_none() {
width = Some(input2.parse()?);
} else {
return Err(input2.error("multiple `width` specifications"));
}
} else if ident == "traits" {
traits.extend(parse_traits(&input2)?);
} else {
return Err(input2.error(format!("unknown key: `{}`", ident)));
}
if !input2.is_empty() {
input2.parse::<Token![;]>()?;
}
}
Ok(Self {
attrs,
ident,
offset: offset.ok_or_else(|| input2.error("missing `offset` specification"))?,
width: width.ok_or_else(|| input2.error("missing `width` specification"))?,
traits,
})
}
}
impl Variant {
#[allow(clippy::too_many_lines, clippy::cognitive_complexity)]
fn generate(&self) -> TokenStream2 {
let t = format_ident!("_T");
let val_ty = format_ident!("u{}", self.size);
let mut imports = self.traits.iter().cloned().collect::<HashSet<_>>();
let mut tokens = Vec::new();
let mut struct_tokens = Vec::new();
let mut ctor_tokens = Vec::new();
for Field { attrs, ident, offset, width, traits } in &self.fields {
let field_snk = ident.to_string().to_snake_case();
let mut field_psc = ident.to_string().to_pascal_case();
if field_psc == "Val" {
field_psc.push('_');
}
let field_psc = format_ident!("{}", field_psc);
let field_ident = format_ident!("{}", unkeywordize(&field_snk));
imports.extend(traits.iter().cloned());
struct_tokens.push(quote! {
#(#attrs)*
pub #field_ident: #field_psc<#t>
});
ctor_tokens.push(quote! {
#field_ident: ::drone_core::token::Token::take()
});
tokens.push(quote! {
#(#attrs)*
#[derive(Clone, Copy)]
pub struct #field_psc<#t: ::drone_core::reg::tag::RegTag>(#t);
unsafe impl<#t> ::drone_core::token::Token for #field_psc<#t>
where
#t: ::drone_core::reg::tag::RegTag,
{
#[inline]
unsafe fn take() -> Self {
#field_psc(#t::default())
}
}
impl<#t> ::drone_core::reg::field::RegField<#t> for #field_psc<#t>
where
#t: ::drone_core::reg::tag::RegTag,
{
type Reg = Reg<#t>;
type URegField = #field_psc<::drone_core::reg::tag::Urt>;
type SRegField = #field_psc<::drone_core::reg::tag::Srt>;
type CRegField = #field_psc<::drone_core::reg::tag::Crt>;
const OFFSET: usize = #offset;
const WIDTH: usize = #width;
}
});
for ident in traits {
tokens.push(quote! {
impl<#t: ::drone_core::reg::tag::RegTag> #ident<#t> for #field_psc<#t> {}
});
}
if width.base10_digits() == "1" {
tokens.push(quote! {
impl<#t> ::drone_core::reg::field::RegFieldBit<#t> for #field_psc<#t>
where
#t: ::drone_core::reg::tag::RegTag,
{
}
});
if traits.iter().any(|name| name == "RRRegField") {
tokens.push(quote! {
impl<'a, #t: ::drone_core::reg::tag::RegTag> Hold<'a, #t> {
#(#attrs)*
#[inline]
pub fn #field_ident(&self) -> bool {
::drone_core::reg::field::RRRegFieldBit::read(
&self.reg.#field_ident,
&self.val,
)
}
}
});
}
if traits.iter().any(|name| name == "WWRegField") {
let set_field = format_ident!("set_{}", field_snk);
let clear_field = format_ident!("clear_{}", field_snk);
let toggle_field = format_ident!("toggle_{}", field_snk);
tokens.push(quote! {
impl<'a, #t: ::drone_core::reg::tag::RegTag> Hold<'a, #t> {
#(#attrs)*
#[inline]
pub fn #set_field(&mut self) -> &mut Self {
::drone_core::reg::field::WWRegFieldBit::set(
&self.reg.#field_ident,
&mut self.val,
);
self
}
#(#attrs)*
#[inline]
pub fn #clear_field(&mut self) -> &mut Self {
::drone_core::reg::field::WWRegFieldBit::clear(
&self.reg.#field_ident,
&mut self.val,
);
self
}
#(#attrs)*
#[inline]
pub fn #toggle_field(&mut self) -> &mut Self {
::drone_core::reg::field::WWRegFieldBit::toggle(
&self.reg.#field_ident,
&mut self.val,
);
self
}
}
});
}
} else {
tokens.push(quote! {
impl<#t> ::drone_core::reg::field::RegFieldBits<#t> for #field_psc<#t>
where
#t: ::drone_core::reg::tag::RegTag,
{
}
});
if traits.iter().any(|name| name == "RRRegField") {
tokens.push(quote! {
impl<'a, #t: ::drone_core::reg::tag::RegTag> Hold<'a, #t> {
#(#attrs)*
#[inline]
pub fn #field_ident(&self) -> #val_ty {
::drone_core::reg::field::RRRegFieldBits::read(
&self.reg.#field_ident,
&self.val,
)
}
}
});
}
if traits.iter().any(|name| name == "WWRegField") {
let write_field = format_ident!("write_{}", field_snk);
tokens.push(quote! {
impl<'a, #t: ::drone_core::reg::tag::RegTag> Hold<'a, #t> {
#(#attrs)*
#[inline]
pub fn #write_field(&mut self, bits: #val_ty) -> &mut Self {
::drone_core::reg::field::WWRegFieldBits::write(
&self.reg.#field_ident,
&mut self.val,
bits,
);
self
}
}
});
}
}
}
if self.fields.is_empty() {
struct_tokens.push(quote!(_marker: ::core::marker::PhantomData<#t>));
ctor_tokens.push(quote!(_marker: ::core::marker::PhantomData));
}
for ident in &self.traits {
tokens.push(quote! {
impl<#t: ::drone_core::reg::tag::RegTag> #ident<#t> for Reg<#t> {}
});
}
let imports = if imports.is_empty() {
quote!()
} else {
let imports = imports.iter();
quote!(use super::{#(#imports),*};)
};
let Variant { attrs, vis, address, reset, .. } = self;
let reg_full = self.reg_full();
quote! {
#(#attrs)*
#vis mod #reg_full {
#imports
use ::drone_core::bitfield::Bitfield;
#(#attrs)*
#[derive(Bitfield, Clone, Copy)]
pub struct Val(#val_ty);
#(#attrs)*
#[derive(Clone, Copy)]
pub struct Reg<#t: ::drone_core::reg::tag::RegTag> {
#(#struct_tokens),*
}
unsafe impl<#t: ::drone_core::reg::tag::RegTag> ::drone_core::token::Token for Reg<#t> {
#[inline]
unsafe fn take() -> Self {
Self { #(#ctor_tokens,)* }
}
}
impl<#t: ::drone_core::reg::tag::RegTag> ::drone_core::reg::Reg<#t> for Reg<#t> {
type Val = Val;
type UReg = Reg<::drone_core::reg::tag::Urt>;
type SReg = Reg<::drone_core::reg::tag::Srt>;
type CReg = Reg<::drone_core::reg::tag::Crt>;
const ADDRESS: usize = #address;
const RESET: #val_ty = #reset;
#[inline]
unsafe fn val_from(bits: #val_ty) -> Val {
Val(bits)
}
}
impl<'a, #t> ::drone_core::reg::RegRef<'a, #t> for Reg<#t>
where
#t: ::drone_core::reg::tag::RegTag + 'a,
{
type Hold = Hold<'a, #t>;
#[inline]
fn hold(&'a self, val: Self::Val) -> Self::Hold {
Hold { reg: self, val }
}
}
#(#attrs)*
pub struct Hold<'a, #t: ::drone_core::reg::tag::RegTag> {
reg: &'a Reg<#t>,
val: Val,
}
impl<'a, #t> ::drone_core::reg::RegHold<'a, #t, Reg<#t>> for Hold<'a, #t>
where
#t: ::drone_core::reg::tag::RegTag,
{
#[inline]
fn val(&self) -> Val {
self.val
}
#[inline]
fn set_val(&mut self, val: Val) {
self.val = val;
}
}
#(#tokens)*
}
}
}
fn reg_full(&self) -> Ident {
format_ident!(
"{}_{}",
self.block.to_string().to_snake_case(),
self.ident.to_string().to_snake_case()
)
}
}
fn parse_traits(input: ParseStream<'_>) -> Result<Vec<Ident>> {
let mut traits = Vec::new();
let input2;
braced!(input2 in input);
while !input2.is_empty() {
traits.push(input2.parse()?);
}
Ok(traits)
}
pub fn proc_macro(input: TokenStream) -> TokenStream {
let Input { variants } = parse_macro_input!(input);
let reg_tokens = variants.iter().map(Variant::generate).collect::<Vec<_>>();
let mut variant_tokens = Vec::new();
for (i, reg_src) in variants.iter().enumerate() {
for (j, reg_dst) in variants.iter().enumerate() {
if i == j {
continue;
}
let t = format_ident!("_T");
let mod_src = reg_src.reg_full();
let mod_dst = reg_dst.reg_full();
let into_variant = format_ident!(
"into_{}_{}",
reg_dst.block.to_string().to_snake_case(),
reg_dst.ident.to_string().to_snake_case()
);
let doc = LitStr::new(
&format!(
"Converts the token of variant `{}`, to a token of variant `{}`.",
mod_src, mod_dst
),
Span::call_site(),
);
variant_tokens.push(quote! {
impl<#t: ::drone_core::reg::tag::RegTag> #mod_src::Reg<#t> {
#[doc = #doc]
pub fn #into_variant(self) -> #mod_dst::Reg<#t> {
unsafe { ::drone_core::token::Token::take() }
}
}
});
}
}
let expanded = quote! {
#(#reg_tokens)*
#(#variant_tokens)*
};
expanded.into()
}