1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#![feature(i128_type, plugin_registrar, rustc_private, quote)]

extern crate syntax;
extern crate rustc;
extern crate rustc_plugin;

mod field;
mod misc;

use rustc_plugin::Registry;
use syntax::codemap::Span;
use syntax::ext::base::{ExtCtxt, MacEager, MacResult};
use syntax::parse::common::SeqSep;
use syntax::parse::token;
use syntax::symbol::keywords;
use syntax::tokenstream;
use syntax::util::small_vector::SmallVector;

use field::parse_field;
use misc::*;

fn expand_bitfield(cx: &mut ExtCtxt,
                   _sp: Span,
                   tts: &[tokenstream::TokenTree])
                   -> Box<MacResult + 'static> {

    let mut parser = cx.new_parser_from_tts(tts);
    let attrs = parser.parse_outer_attributes().unwrap();
    let is_pub = parser.eat_keyword(keywords::Pub);
    let struct_ident = match parser.parse_ident() {
        Ok(ident) => ident,
        Err(mut e) => {
            e.emit();
            parser.abort_if_errors();
            unreachable!();
        }
    };

    if let Err(mut e) = parser.expect(&token::Comma) {
        e.emit();
    }

    let sep = SeqSep {
        sep: Some(token::Comma),
        trailing_sep_allowed: true,
    };

    let fields = match parser.parse_seq_to_end(&token::Eof, sep, |p| Ok(parse_field(p))) {
        Ok(fields) => fields,
        Err(mut e) => {
            e.emit();
            vec![]
        }
    };

    let mut const_new = false;
    let mut pub_data = false;
    let attrs = attrs
        .into_iter()
        .filter(|a| if a.path == "const_new" {
                    const_new = true;
                    false
                } else if a.path == "pub_data" {
            pub_data = true;
            false
        } else {
            true
        })
        .collect();
    let mut methods = Vec::with_capacity(fields.len() * 2 + 1);
    let bit_length = fields.iter().fold(0, |a, b| a + b.bit_len());
    let byte_length = ((bit_length + 7) / 8) as usize;
    let maybe_pub = make_maybe_pub(is_pub);
    let maybe_pub_data = make_maybe_pub(pub_data);
    let maybe_const_new = make_maybe_ident(const_new, "const");
    let struct_decl = if byte_length > 0 {
        let new_doc = format!("Creates a new `{}`", struct_ident);
        let method_new = quote_item!(cx,
            impl $struct_ident {
                #[doc = $new_doc]
                $maybe_pub $maybe_const_new fn new(data: [u8; $byte_length]) -> $struct_ident {
                    $struct_ident { data: data}
                }
            })
                .unwrap();
        methods.push(method_new);
        quote_item!(cx, $maybe_pub struct $struct_ident { $maybe_pub_data data: [u8; $byte_length]};).unwrap()
    } else {
        quote_item!(cx, $maybe_pub struct $struct_ident { };).unwrap()
    };
    let struct_decl = struct_decl.map(|mut s| {
                                          s.attrs = attrs;
                                          s
                                      });

    let mut field_start = 0;
    for field in fields {
        methods.extend(field.to_methods(cx, struct_ident, field_start));
        field_start += field.bit_len();
    }

    let items = if !methods.is_empty() {
        vec![struct_decl, merge_impls(methods)]
    } else {
        vec![struct_decl]
    };
    let s = SmallVector::many(items);
    MacEager::items(s)
}

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_macro("bitfield", expand_bitfield);
}