bf_impl/
lib.rs

1#![recursion_limit="128"]
2extern crate proc_macro;
3extern crate proc_macro2;
4extern crate quote;
5extern crate syn;
6
7use proc_macro::TokenStream;
8use proc_macro2::Span;
9use quote::{quote, ToTokens};
10use syn::{
11    parse, parse::{Parse, ParseStream},
12    punctuated::Punctuated,
13    Ident, Visibility, Type, LitInt, token,
14    Token, bracketed, braced
15};
16
17#[derive(Clone)]
18struct BfField {
19    vis: Visibility,
20    name: Ident,
21    start_pos: LitInt,
22    end_pos: LitInt,
23}
24
25impl Parse for BfField {
26    fn parse(stream: ParseStream) -> Result<Self, parse::Error> {
27        let (vis, name) = if stream.fork().parse::<Visibility>().is_ok() {
28            (stream.parse()?, stream.parse()?)
29        } else {
30            (Visibility::Inherited, stream.parse()?)
31        };
32        token::Colon::parse(stream)?;
33
34        let start_pos = stream.parse()?;
35        token::Colon::parse(stream)?;
36        let end_pos = stream.parse()?;
37
38        Ok(Self {
39            vis: vis,
40            name: name,
41            start_pos: start_pos,
42            end_pos: end_pos
43        })
44    }
45}
46
47#[derive(Clone)]
48struct BfInfo {
49    vis: Visibility,
50    name: Ident,
51    ty: Type,
52    fields: Vec<BfField>
53}
54
55impl Parse for BfInfo {
56    fn parse(stream: ParseStream) -> Result<Self, parse::Error> {
57        let (vis, name) = if stream.fork().parse::<Visibility>().is_ok() {
58            (stream.parse()?, stream.parse()?)
59        } else {
60            (Visibility::Inherited, stream.parse()?)
61        };
62
63        let ty_inner;
64        bracketed!(ty_inner in stream);
65        let ty = ty_inner.parse()?;
66
67        let fields_inner;
68        braced!(fields_inner in stream);
69
70        let field_parser = Punctuated::<BfField, Token![,]>::parse_terminated;
71        let fields = field_parser(&fields_inner)?;
72        let fields = fields.iter().cloned().collect();
73
74        Ok(Self {
75            vis: vis,
76            name: name,
77            ty: ty,
78            fields: fields
79        })
80    }
81}
82
83fn make_accessor(ty: &Type, field: &BfField) -> impl ToTokens {
84    let BfField { vis, name, start_pos, end_pos } = field;
85    let set_name = Ident::new(&format!("set_{}", name), Span::call_site());
86    let upd_name = Ident::new(&format!("upd_{}", name), Span::call_site());
87
88    let base_mask_const = quote!(
89        const BASE_MASK: #ty = (1 << (#end_pos - #start_pos + 1)) - 1;
90    );
91
92    quote!(
93        #[inline(always)]
94        #[allow(dead_code)]
95        #vis fn #name(&self) -> #ty {
96            #base_mask_const
97            (self.val >> #start_pos) & BASE_MASK
98        }
99
100        #[inline(always)]
101        #[allow(dead_code)]
102        #vis fn #set_name(&mut self, val: #ty) -> &mut Self {
103            #base_mask_const
104            self.val &= !(BASE_MASK << #start_pos);
105            self.val |= (val & BASE_MASK) << #start_pos;
106            self
107        }
108
109        #[inline(always)]
110        #[allow(dead_code)]
111        #vis fn #upd_name<F>(&mut self, func: F) -> &mut Self
112            where F: FnOnce(#ty) -> #ty {
113            let old = self.#name();
114            self.#set_name(func(old))
115        }
116    )
117}
118
119#[cfg(feature="use_std")]
120fn make_debug<'a>(name: &Ident, fields: impl Iterator<Item=&'a Ident>) -> impl ToTokens {
121    let (field_names, fields): (Vec<_>, Vec<_>) = fields.map(|f| (f.to_string(), f)).unzip();
122    quote!(
123        impl ::std::fmt::Debug for #name {
124            fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
125                f.debug_struct(stringify!(#name))
126                    #(.field(#field_names, &self.#fields()))*
127                    .finish()
128            }
129        }
130    )
131}
132
133
134#[cfg(not(feature="use_std"))]
135fn make_debug<'a>(_: &Ident, _: impl Iterator<Item=&'a Ident>) -> impl ToTokens {
136    quote!()
137}
138
139#[proc_macro]
140pub fn bf(tok: TokenStream) -> TokenStream {
141    let bfinfo: BfInfo = parse(tok).unwrap();
142    let BfInfo{vis, name, ty, fields} = bfinfo;
143    let accessors = fields.iter()
144        .map(|f| make_accessor(&ty, f));
145
146    let fmt = make_debug(&name, fields.iter().map(|f| &f.name));
147    
148    quote!(
149        #[derive(Copy, Clone)]
150        #[repr(transparent)]
151        #vis struct #name {
152            pub val: #ty
153        }
154
155        impl #name {
156            #[inline(always)]
157            pub fn new(val: #ty) -> Self {
158                Self { val: val }
159            }
160
161            #[allow(dead_code)]
162            #[inline(always)]
163            pub fn alias<'a>(val: &'a #ty) -> &'a Self {
164                unsafe { &*(val as *const #ty as *const Self) }
165            }
166
167            #[allow(dead_code)]
168            #[inline(always)]
169            pub fn alias_mut<'a>(val: &'a mut #ty) -> &'a mut Self {
170                unsafe { &mut *(val as *mut #ty as *mut Self) }
171            }
172
173            #(#accessors)*
174        }
175
176        #fmt
177    ).into()
178}