GDP Rust Retrospective 2018-09-10
In this post:
- CODE
- How the week went
- Future goals
After the last post, I read over The Little Book of Rust Macros some more, and figured out what I was doing wrong and how to make the macros properly expressive. Then, I re-did the documentation, and added examples. The result is... lengthy. I'm not sure I can properly convey it, so here's the file:
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 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 | /// Create a named struct—that is, a struct that associates a Name to data. /// This macro is invoked on any number of "items". /// For the purposes of this macro, an "item" is one of: /// a struct definition, or an impl block. /// /// A struct definition consists of zero or more attributes, /// followed by an optional visibility specifier, /// followed by `struct`, /// followed by a name, /// followed by two type parameters with no bounds, /// followed by a semicolon. /// /// Note: the macro adds many derived trait impls. /// `Default` is not one of them, and implementing `Default` is not recommended. /// /// An impl block consists of `impl`, /// followed by two type parameters, /// still with no bounds, /// followed by a name, /// followed by two type parameters, /// followed by a block. /// The block contains method stubs. /// /// Method stubs consist of a visibility specifier /// (with allowable values constrained by the method name), /// followed by `fn`, /// followed by the name (one of a predefined set of choices), /// followed by any required type parameters, /// followed by a semicolon. /// /// The allowed method names are: /// /// * `new`: cannot have bare `pub` for visibility, /// `pub(crate)` recommended, /// takes no type parameters. /// `new(value)` constructs a value of type `Self`, wrapping `value`. /// * `as_mut`: cannot have bare `pub` for visibility, /// `pub(crate)` recommended, /// takes no type parameters. /// `self.as_mut()` returns a mutable reference to the wrapped value. /// * `into_inner`: can have any visibility, /// `pub` recommended, /// takes no type parameters. /// `self.into_inner()` consumes `self` and returns the wrapped value. /// * `as_ref`: can have any visibility, /// `pub` recommended, /// takes no type parameters. /// `self.as_ref()` returns an immutable reference to the wrapped value. /// /// # Examples /// /// ```rust /// # #[macro_use] /// # extern crate gdp; /// # fn main() { /// named_struct! { /// struct MyNamed<N, T>; /// /// impl<N, T> MyNamed<N, T> { /// pub(crate) fn new; // Define as needed. /// /// pub(crate) fn as_mut; // Define as needed. /// /// pub fn into_inner; // Definition recommended. /// /// pub fn as_ref; // Definition recommended. /// } /// } /// # } /// ``` #[macro_export] macro_rules! named_struct { ($vis:vis struct $name:ident<$N:ident, $T:ident $(,)*>; $($tail:tt)*) => { #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] $vis struct $name<$N, $T>($crate::pd_wrapper::PDWrapper<$N>, $T); named_struct!{$($tail)*} }; // Method block (@$param:ident pub fn new $($tail:tt)*) => { compile_error!("Generated `new` method cannot be public"); }; (@$param:ident pub fn as_mut $($tail:tt)*) => { compile_error!("Generated `as_mut` method cannot be public"); }; (@$param:ident $vis:vis fn new; $($tail:tt)*) => { $vis fn new(t: $param) -> Self { Self{0: $crate::pd_wrapper::PDWrapper::new(), 1: t} } named_struct!{@$param $($tail)*} }; (@$param:ident $vis:vis fn into_inner; $($tail:tt)*) => { $vis fn into_inner(self) -> $param { self.1 } named_struct!{@$param $($tail)*} }; (@$param:ident $vis:vis fn as_ref; $($tail:tt)*) => { $vis fn as_ref(&self) -> &$param { &self.1 } named_struct!{@$param $($tail)*} }; (@$param:ident $vis:vis fn as_mut; $($tail:tt)*) => { $vis fn as_mut(&mut self) -> &mut $param { &mut self.1 } named_struct!{@$param $($tail)*} }; // End method block (impl<$N1:ident, $T1:ident $(,)*> $name:ident<$N2:ident, $T2:ident $(,)*> {$($tail1:tt)*} $($tail2:tt)*) => { impl<$N1, $T1> $name<$N2, $T2> { named_struct!{@$T2 $($tail1)*} } named_struct!{$($tail2)*} }; ($(#[$outer:meta])+ $($tail:tt)*) => {$(#[$outer])* named_struct!{$($tail)*}}; (@$param:ident $(#[$outer:meta])+ $($tail:tt)*) => {$(#[$outer])* named_struct!{@$param $($tail)*}}; (@$_:ident) => {}; () => {}; } /// Create a name struct—that is, a struct that implements the `Name` trait. /// This macro is invoked on any number of "items". /// For the purposes of this macro, an "item" is one of: /// a struct definition, or an impl block. /// /// A struct definition consists of zero or more attributes, /// followed by an optional visibility specifier, /// followed by `struct`, /// followed by a name, /// followed by optional type parameters with no bounds, /// followed by a semicolon. /// /// Note: the macro adds many derived trait impls. /// `Default` is not one of them, and implementing `Default` is not recommended. /// /// An impl block consists of `impl`, /// followed by the appropriate number of type parameters, /// still with no bounds, /// followed by a name, /// followed by the appropriate number of type parameters, /// followed by a block. /// The block contains method stubs. /// /// Method stubs consist of a visibility specifier /// (with allowable values constrained by the method name), /// followed by `fn`, /// followed by the name (one of a predefined set of choices), /// followed by any required type parameters, /// followed by a semicolon. /// /// The allowed method names are: /// /// * `defn`: cannot have bare `pub` for visibility, /// `pub(crate)` recommended, /// takes one unbounded type parameter, /// does not introduce bounds. /// `defn<T>(value: T)` constructs an instance of `Named<Self, T>`, /// wrapping `value`. /// /// Note: all generated `impl`s, including the `Name` impl, /// insert a bound of `Name` for `impl`-level type parameters. /// /// # Examples /// /// ```rust /// # #[macro_use] /// # extern crate gdp; /// # fn main() { /// name! { /// struct Nullary; /// /// impl Nullary { /// pub(crate) fn defn<T>; // Define as needed. /// } /// /// struct Unary<A>; /// /// impl<A> Unary<A> { /// pub(crate) fn defn<T>; // Define as needed. /// } /// /// struct Binary<A, B>; /// /// impl<A, B> Binary<A, B> { /// pub(crate) fn defn<T>; // Define as needed. /// } /// } /// # } /// ``` #[macro_export] macro_rules! name { ($vis:vis struct $name:ident<$($param:ident),* $(,)*>; $($tail:tt)*) => { #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] $vis struct $name<$($param,)*>($crate::pd_wrapper::PDWrapper<($($param,)*)>); impl<$($param: $crate::named::Name,)*> $crate::named::Name for $name<$($param,)*> {} name!{$($tail)*} }; ($vis:vis struct $name:ident; $($tail:tt)*) => {name!{$vis struct $name<>; $($tail)*}}; // Method block (pub fn defn $($tail:tt)*) => { compile_error!("Generated `defn` method cannot be public"); }; ($vis:vis fn defn<$param:ident>; $($tail:tt)*) => { $vis fn defn<$param>(t: $param) -> $crate::named::Named<Self, $param> { $crate::named::defn(Self{0: $crate::pd_wrapper::PDWrapper::new()}, t) } }; // End method block (impl<$($param1:ident),* $(,)*> $name:ident<$($param2:ident),* $(,)*> {$($tail1:tt)*} $($tail2:tt)*) => { impl<$($param1: $crate::named::Name),*> $name<$($param2),*> { name!{$($tail1)*} } name!{$($tail2)*} }; (impl<$($param1:ident),* $(,)*> $name:ident $($tail:tt)*) => {name!{impl<$($param1),*> $name<> $($tail)*}}; (impl $($tail:tt)*) => {name!{impl<> $($tail)*}}; ($(#[$outer:meta])+ $($tail:tt)*) => {$(#[$outer])* name!{$($tail)*}}; () => {}; } |
EDIT: Ugh, something is wrong with the style sheet, I want to fix it...
I don't know how much of this length is How Macros Are, and how much is that these are the first big macros I've written, so of course it's probably longer than it could be.
Regardless, I'm past the big scary thing. Now I can focus on documenting everything else, made possible by following the API guidelines.
Anyway, so far as what I accomplished this week, I got these macros written with their cool input format, and documented them. Once I'd realized how bad my initial macro formats were, that was about all I could hope for.
Moving forward, I want to get the rest of the base crate up to standards, then go over sorted-by some more. Once I've hit the basic implementation goals for sorted-by, I want to see what kind of code it's generating. When I was messing around with a much earlier version of this code, it didn't look to me like the output was structured in a zero-cost way, relative to equivalent code.
Next week, I'm going to try to take it easy.