Skip to content

Instantly share code, notes, and snippets.

@durka
Forked from anonymous/playground.rs
Last active September 24, 2015 03:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save durka/763519a8403b229750c5 to your computer and use it in GitHub Desktop.
Save durka/763519a8403b229750c5 to your computer and use it in GitHub Desktop.
Rust tt-muncher macro implementing C++-like visibility labels for struct members
/// Wrapper for a struct declaration using C++-like "pub:" and "priv:" labels instead of Rust's individual member annotations
///
/// Syntax is similar to a normal pub struct declaration (see example below)
/// The struct is given an automatic pub fn new() method which simply takes all members in order -- without this, there would be no way to construct an instance due to the private members
macro_rules! sticky_visibility {
// START INTERNAL RULES
// defeat the parser
(@as_item $i:item) => ($i);
// following rules are an incremental tt-munching parser for the input struct body
// $cur tracks the current visibility state
// accumulators build up the output struct body ($members), plus the element names ($elemname) and types ($elemtyp) which we will need to implement new()
// end of the input struct body: output the struct and an impl containing new()
(@parse [$typ:ident] $_cur:tt
$members:tt [$($elemname:ident),*] [$($elemtyp:ty),*]
{ $(,)* }
) => {
sticky_visibility! { @as_item
pub struct $typ $members
}
impl $typ {
pub fn new($($elemname: $elemtyp),*) -> $typ {
$typ {
$($elemname: $elemname),*
}
}
}
};
// switch the current visibility to public
(@parse $t_:tt $_cur:tt
$members:tt
$en_:tt $et_:tt
{ pub: $($body:tt)* }
) => {
sticky_visibility! { @parse $t_ [pub]
$members
$en_ $et_
{ $($body)* }
}
};
// switch the current visibility to private
(@parse $t_:tt $_cur:tt
$members:tt
$en_:tt $et_:tt
{ priv: $($body:tt)* }
) => {
sticky_visibility! { @parse $t_ []
$members
$en_ $et_
{ $($body)* }
}
};
// eat a struct member (not the last one)
(@parse $t_:tt [$($cur:ident)*]
{ $($members:tt)* }
[$($elemname:ident),*] [$($elemtyp:ty),*]
{ $n:ident: $t:ty, $($body:tt)* }
) => {
sticky_visibility! { @parse $t_ [$($cur)*]
{ $($members)* $($cur)* $n: $t, }
[$($elemname,)* $n] [$($elemtyp,)* $t]
{ $($body)* }
}
};
// eat the last struct member
(@parse $t_:tt [$($cur:ident)*]
{ $($members:tt)* }
[$($elemname:ident),*] [$($elemtyp:ty),*]
{ $n:ident: $t:ty }
) => {
sticky_visibility! { @parse $t_ [$($cur)*]
{ $($members)* $($cur)* $n: $t, }
[$($elemname,)* $n] [$($elemtyp,)* $t]
{ }
}
};
// END INTERNAL RULES
// macro entry point
(pub struct $typ:ident { $($body:tt)* }) => {
sticky_visibility!(@parse [$typ] [] {} [] [] { $($body)* });
};
}
// there's no point testing visibility without an inner module
mod inner {
sticky_visibility! {
// struct must be public (otherwise why would you care about visibility?)
pub struct Foo { // no generics allowed (limitation of the current macro)
pub:
a: i32,
b: bool, // this comma required (fundamental limitation of macro syntax)
priv:
c: (i32, bool),
d: char, // this comma not required
}
}
}
use inner::Foo;
fn main() {
// constructor works ok, but let's try accessing the members...
let foo = Foo::new(42, true, (1, false), '\0');
println!("{:?} {:?}", foo.a, foo.b); // ok
println!("{:?} {:?}", foo.c, foo.d); // error
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment