diff --git a/ioreg/src/builder/accessors.rs b/ioreg/src/builder/accessors.rs index 3976d198..fcccee2c 100644 --- a/ioreg/src/builder/accessors.rs +++ b/ioreg/src/builder/accessors.rs @@ -37,6 +37,10 @@ impl<'a> node::RegVisitor for BuildAccessors<'a> { let item = build_get_fn(self.cx, path, reg); self.builder.push_item(item); } + if fields.iter().any(|f| f.access != node::Access::ReadOnly) { + let item = build_ignoring_state_setter_fn(self.cx, path, reg); + self.builder.push_item(item); + } for field in fields.iter() { match build_field_accessors(self.cx, path, reg, field) { @@ -121,6 +125,29 @@ fn build_get_fn(cx: &ExtCtxt, path: &Vec, reg: &node::Reg) item.unwrap() } +fn build_ignoring_state_setter_fn(cx: &ExtCtxt, path: &Vec, reg: &node::Reg) + -> P +{ + let reg_ty: P = + cx.ty_ident(reg.name.span, utils::path_ident(cx, path)); + let setter_ty = utils::setter_name(cx, path); + + let docstring = format!("Create new updater that ignores current value of the `{}` register", + reg.name.node); + let doc_attr = utils::doc_attribute(cx, utils::intern_string(cx, docstring)); + + let item = quote_item!(cx, + impl $reg_ty { + $doc_attr + #[allow(dead_code)] + pub fn ignoring_state(&self) -> $setter_ty { + $setter_ty::new_ignoring_state(self) + } + } + ); + item.unwrap() +} + fn build_field_set_fn(cx: &ExtCtxt, path: &Vec, reg: &node::Reg, field: &node::Field) -> P diff --git a/ioreg/src/builder/setter.rs b/ioreg/src/builder/setter.rs index d8623938..02a16075 100644 --- a/ioreg/src/builder/setter.rs +++ b/ioreg/src/builder/setter.rs @@ -76,10 +76,11 @@ fn build_type(cx: &ExtCtxt, path: &Vec, let item = quote_item!(cx, $doc_attr - #[allow(non_camel_case_types)] + #[allow(non_camel_case_types, dead_code)] pub struct $name<'a> { value: $packed_ty, mask: $packed_ty, + write_only: bool, reg: &'a $reg_ty, } ); @@ -100,6 +101,27 @@ fn build_new<'a>(cx: &'a ExtCtxt, path: &Vec, reg: &node::Reg) $setter_ident { value: 0, mask: 0, + write_only: false, + reg: reg, + } + } + } + ).unwrap()) +} + +fn build_new_ignoring_state<'a>(cx: &'a ExtCtxt, path: &Vec, + reg: &node::Reg) -> P { + let reg_ty: P = + cx.ty_ident(reg.name.span, utils::path_ident(cx, path)); + let setter_ident = utils::setter_name(cx, path); + utils::unwrap_impl_item(quote_item!(cx, + impl<'a> $setter_ident<'a> { + #[doc="Create a new updater that ignores current state"] + pub fn new_ignoring_state(reg: &'a $reg_ty) -> $setter_ident<'a> { + $setter_ident { + value: 0, + mask: 0, + write_only: true, reg: reg, } } @@ -132,7 +154,7 @@ fn build_drop(cx: &ExtCtxt, path: &Vec, if wo_reg { quote_expr!(cx, 0) } else { - quote_expr!(cx, self.reg.value.get()) + quote_expr!(cx, if self.write_only { 0 } else { self.reg.value.get() }) }; let item = quote_item!(cx, @@ -164,6 +186,7 @@ fn build_impl(cx: &ExtCtxt, path: &Vec, reg: &node::Reg, fields: &Vec) -> P { let new = build_new(cx, path, reg); + let new_is = build_new_ignoring_state(cx, path, reg); let setter_ident = utils::setter_name(cx, path); let methods: Vec> = FromIterator::from_iter( @@ -174,6 +197,7 @@ fn build_impl(cx: &ExtCtxt, path: &Vec, reg: &node::Reg, #[allow(dead_code)] impl<'a> $setter_ident<'a> { $new + $new_is $methods $done } diff --git a/ioreg/src/builder/union.rs b/ioreg/src/builder/union.rs index 45840108..a6fce1d7 100644 --- a/ioreg/src/builder/union.rs +++ b/ioreg/src/builder/union.rs @@ -228,7 +228,20 @@ impl<'a> BuildUnionTypes<'a> { } } ).unwrap(); + let copy_impl = quote_item!(self.cx, impl ::core::marker::Copy for $name {}).unwrap(); - vec!(struct_item, clone_impl, copy_impl) + + let item_address = reg.address; + let item_getter = quote_item!(self.cx, + #[allow(non_snake_case, dead_code)] + pub fn $name() -> &'static $name { + unsafe { ::core::intrinsics::transmute($item_address as usize) } + } + ).unwrap(); + if item_address == 0 { + vec!(struct_item, clone_impl, copy_impl) + } else { + vec!(struct_item, clone_impl, copy_impl, item_getter) + } } } diff --git a/ioreg/src/lib.rs b/ioreg/src/lib.rs index 527ab647..2a420c33 100644 --- a/ioreg/src/lib.rs +++ b/ioreg/src/lib.rs @@ -327,10 +327,9 @@ N => NAME */ -#![feature(quote, plugin_registrar, rustc_private, collections, core)] -#![feature(convert)] -#![feature(plugin)] +#![feature(quote, plugin_registrar, rustc_private, collections, core, convert)] +#![feature(plugin)] #![plugin(syntaxext_lint)] extern crate rustc; diff --git a/ioreg/src/node.rs b/ioreg/src/node.rs index 356c1548..2075b7b1 100644 --- a/ioreg/src/node.rs +++ b/ioreg/src/node.rs @@ -19,7 +19,7 @@ use syntax::codemap::{Spanned, Span}; use syntax::ast; /// A variant of an enum field type -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Variant { pub name: Spanned, pub value: Spanned, @@ -27,7 +27,7 @@ pub struct Variant { } /// A bit field type -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum FieldType { /// A unsigned integer UIntField, @@ -40,7 +40,7 @@ pub enum FieldType { }, } -#[derive(Copy, PartialEq, Eq, Clone)] +#[derive(Copy, PartialEq, Eq, Clone, Debug)] pub enum Access { ReadWrite, ReadOnly, @@ -49,7 +49,7 @@ pub enum Access { SetToClear, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Field { pub name: Spanned, /// The index of the first (lowest order) bit of the field @@ -71,7 +71,7 @@ impl Field { } } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] pub enum RegWidth { /// A 32-bit wide register Reg32, @@ -92,7 +92,7 @@ impl RegWidth { } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum RegType { /// A primitive bitfield RegPrim(Spanned, Vec), @@ -111,13 +111,14 @@ impl RegType { } /// A single register, either a union or primitive -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Reg { pub offset: u64, pub name: Spanned, pub ty: RegType, pub count: Spanned, pub docstring: Option>, + pub address: usize, } impl Reg { diff --git a/ioreg/src/parser.rs b/ioreg/src/parser.rs index 4a0fc42e..a3102786 100644 --- a/ioreg/src/parser.rs +++ b/ioreg/src/parser.rs @@ -76,6 +76,17 @@ impl<'a> Parser<'a> { Some(name) => respan(self.last_span, name), None => return None, }; + let mut address: usize = 0; + + if self.token == token::At { + self.bump(); + + // TODO(farcaller): expect_usize should return usize, no? + address = match self.expect_usize() { + Some(address) => address as usize, + None => return None, + }; + } if !self.expect(&token::Eq) { return None; @@ -99,6 +110,7 @@ impl<'a> Parser<'a> { ty: RegType::RegUnion(Rc::new(regs)), count: respan(mk_sp(sp_lo, self.span.hi), 1), docstring: docstring, + address: address, }; Some(Rc::new(group)) @@ -137,26 +149,29 @@ impl<'a> Parser<'a> { regs.sort_by(|r1,r2| r1.offset.cmp(&r2.offset)); // Verify that registers don't overlap - let mut failed = false; - for (r1,r2) in regs.iter().zip(regs.iter().skip(1)) { - if r2.offset <= r1.last_byte() { - self.sess.span_diagnostic.span_err( - r1.name.span, - format!("The byte range of register ({} to {})", - r1.offset, r1.last_byte()).as_str()); - self.sess.span_diagnostic.span_err( - r2.name.span, - format!("overlaps with the range of this register ({} to {})", - r2.offset, r2.last_byte()).as_str()); - failed = true; - } - } - - if failed { - None - } else { - Some(regs) - } + // TODO(farcaller): they actually can, which is a register union. We need + // a proper terminology and implementation for that. + // let mut failed = false; + // for (r1,r2) in regs.iter().zip(regs.iter().skip(1)) { + // if r2.offset <= r1.last_byte() { + // self.sess.span_diagnostic.span_err( + // r1.name.span, + // format!("The byte range of register ({} to {})", + // r1.offset, r1.last_byte()).as_slice()); + // self.sess.span_diagnostic.span_err( + // r2.name.span, + // format!("overlaps with the range of this register ({} to {})", + // r2.offset, r2.last_byte()).as_slice()); + // failed = true; + // } + // } + + // if failed { + // None + // } else { + // Some(regs) + // } + Some(regs) } /// Parse the introduction of a register @@ -260,6 +275,7 @@ impl<'a> Parser<'a> { ty: ty, count: count, docstring: docstring, + address: 0, }) } diff --git a/ioreg/tests/test.rs b/ioreg/tests/test.rs index 514bebfe..54db9726 100644 --- a/ioreg/tests/test.rs +++ b/ioreg/tests/test.rs @@ -39,7 +39,7 @@ mod test { } } - ioregs!(BASIC_TEST = { + ioregs!(BASIC_TEST @ 0 = { 0x0 => reg32 reg1 { 0 => field1, 1..3 => field2, @@ -125,7 +125,7 @@ mod test { ); */ - ioregs!(GROUP_TEST = { + ioregs!(GROUP_TEST @ 0 = { 0x0 => group regs[5] { 0x0 => reg32 reg1 { 0..31 => field1 @@ -172,7 +172,7 @@ mod test { ); */ - ioregs!(FIELD_ARRAY_TEST = { + ioregs!(FIELD_ARRAY_TEST @ 0 = { 0x0 => reg32 reg1 { 0..31 => field[16] } @@ -208,7 +208,7 @@ mod test { ); */ - ioregs!(GAP_TEST = { + ioregs!(GAP_TEST @ 0 = { 0x0 => reg32 reg1 { 0..31 => field, } @@ -268,7 +268,7 @@ mod test { ); */ - ioregs!(MULTI_TEST = { + ioregs!(MULTI_TEST @ 0 = { 0x100 => reg32 reg1[8] { 0..31 => field[32], }