iota/
lib.rs

1//! [![github]](https://github.com/dtolnay/iota) [![crates-io]](https://crates.io/crates/iota) [![docs-rs]](https://docs.rs/iota)
2//!
3//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
4//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
5//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs
6//!
7//! <br>
8//!
9//! The `iota!` macro constructs a set of related constants.
10//!
11//! ```
12//! use iota::iota;
13//!
14//! iota! {
15//!     const A: u8 = 1 << iota;
16//!         , B
17//!         , C
18//!         , D
19//! }
20//!
21//! fn main() {
22//!     assert_eq!(A, 1);
23//!     assert_eq!(B, 2);
24//!     assert_eq!(C, 4);
25//!     assert_eq!(D, 8);
26//! }
27//! ```
28//!
29//! Within an `iota!` block, the `iota` variable is an untyped integer constant
30//! whose value begins at 0 and increments by 1 for every constant declared in
31//! the block.
32//!
33//! ```
34//! use iota::iota;
35//!
36//! iota! {
37//!     const A: u8 = 1 << iota;
38//!         , B
39//!
40//!     const C: i32 = -1; // iota is not used but still incremented
41//!
42//!     pub const D: u8 = iota * 2;
43//!         , E
44//!         , F
45//! }
46//!
47//! // `iota` begins again from 0 in this block
48//! iota! {
49//!     const G: usize = 1 << (iota + 10);
50//!         , H
51//! }
52//!
53//! fn main() {
54//!     assert_eq!(A, 1 << 0);
55//!     assert_eq!(B, 1 << 1);
56//!
57//!     assert_eq!(C, -1);
58//!
59//!     assert_eq!(D, 3 * 2);
60//!     assert_eq!(E, 4 * 2);
61//!     assert_eq!(F, 5 * 2);
62//!
63//!     assert_eq!(G, 1 << (0 + 10));
64//!     assert_eq!(H, 1 << (1 + 10));
65//! }
66//! ```
67
68#![allow(clippy::needless_doctest_main)]
69
70/// Please refer to the crate-level documentation.
71#[macro_export]
72macro_rules! iota {
73    (const $n:ident : $t:ty = $($rest:tt)+) => {
74        $crate::__iota_dup!((0) const $n : $t = $($rest)+);
75    };
76
77    (pub const $n:ident : $t:ty = $($rest:tt)+) => {
78        $crate::__iota_dup!((0) pub const $n : $t = $($rest)+);
79    };
80}
81
82// Duplicate the input tokens so we can match on one set and trigger errors on
83// the other set.
84#[macro_export]
85#[doc(hidden)]
86macro_rules! __iota_dup {
87    (($v:expr)) => {};
88
89    (($v:expr) const $n:ident : $t:ty = $($rest:tt)+) => {
90        $crate::__iota_impl!(($v) () () const $n : $t = ($($rest)+) ($($rest)+));
91    };
92
93    (($v:expr) pub const $n:ident : $t:ty = $($rest:tt)+) => {
94        $crate::__iota_impl!(($v) () (pub) const $n : $t = ($($rest)+) ($($rest)+));
95    };
96}
97
98#[macro_export]
99#[doc(hidden)]
100macro_rules! __iota_impl {
101    // ERROR: Premature semicolon.
102    //
103    //    const A: u8 = ;
104    (($v:expr) () $vis:tt const $n:ident : $t:ty = (; $($x:tt)*) ($semi:tt $($y:tt)*)) => {
105        // "no rules expected the token `;`"
106        $crate::__iota_impl!($semi);
107    };
108
109    // ERROR: Unexpected const, probably due to a missing semicolon.
110    //
111    //    const A: u8 = 1 << iota
112    //    const B: u8 = 0;
113    (($v:expr) ($($seen:tt)*) $vis:tt const $n:ident : $t:ty = (const $($x:tt)*) ($cons:tt $($y:tt)*)) => {
114        // "no rules expected the token `const`"
115        $crate::__iota_impl!($cons);
116    };
117
118    // ERROR: Missing final semicolon.
119    //
120    //    const A: u8 = 1 << iota
121    (($v:expr) ($($seen:tt)*) $vis:tt const $n:ident : $t:ty = () $y:tt) => {
122        // "unexpected end of macro invocation"
123        $crate::__iota_impl!();
124    };
125
126    // OK: Emit a const and reuse the same expression for the next one.
127    //
128    //    const A: u8 = 1 << iota;
129    //        , B
130    (($v:expr) ($($seen:tt)+) ($($vis:tt)*) const $n:ident : $t:ty = (; , $i:ident $($rest:tt)*) $y:tt) => {
131        $($vis)* const $n : $t = $crate::__iota_replace!(($v) (()) $($seen)+);
132        $crate::__iota_impl!(($v + 1) ($($seen)+) ($($vis)*) const $i : $t = (; $($rest)*) (; $($rest)*));
133    };
134
135    // OK: Emit a const and use a different expression for the next one, if any.
136    (($v:expr) ($($seen:tt)+) ($($vis:tt)*) const $n:ident : $t:ty = (; $($rest:tt)*) $y:tt) => {
137        $($vis)* const $n : $t = $crate::__iota_replace!(($v) (()) $($seen)+);
138        $crate::__iota_dup!(($v + 1) $($rest)*);
139    };
140
141    // OK: Munch a token into the expression for the current const.
142    (($v:expr) ($($seen:tt)*) $vis:tt const $n:ident : $t:ty = ($first:tt $($rest:tt)*) $y:tt) => {
143        $crate::__iota_impl!(($v) ($($seen)* $first) $vis const $n : $t = ($($rest)*) ($($rest)*));
144    };
145
146    // DONE.
147    (($v:expr) ()) => {};
148}
149
150#[macro_export]
151#[doc(hidden)]
152macro_rules! __iota_replace {
153    // Open parenthesis.
154    (($v:expr) ($($stack:tt)*) ($($first:tt)*) $($rest:tt)*) => {
155        $crate::__iota_replace!(($v) (() $($stack)*) $($first)* __iota_close_paren $($rest)*)
156    };
157
158    // Open square bracket.
159    (($v:expr) ($($stack:tt)*) [$($first:tt)*] $($rest:tt)*) => {
160        $crate::__iota_replace!(($v) (() $($stack)*) $($first)* __iota_close_bracket $($rest)*)
161    };
162
163    // Open curly brace.
164    (($v:expr) ($($stack:tt)*) {$($first:tt)*} $($rest:tt)*) => {
165        $crate::__iota_replace!(($v) (() $($stack)*) $($first)* __iota_close_brace $($rest)*)
166    };
167
168    // Close parenthesis.
169    (($v:expr) (($($close:tt)*) ($($top:tt)*) $($stack:tt)*) __iota_close_paren $($rest:tt)*) => {
170        $crate::__iota_replace!(($v) (($($top)* ($($close)*)) $($stack)*) $($rest)*)
171    };
172
173    // Close square bracket.
174    (($v:expr) (($($close:tt)*) ($($top:tt)*) $($stack:tt)*) __iota_close_bracket $($rest:tt)*) => {
175        $crate::__iota_replace!(($v) (($($top)* [$($close)*]) $($stack)*) $($rest)*)
176    };
177
178    // Close curly brace.
179    (($v:expr) (($($close:tt)*) ($($top:tt)*) $($stack:tt)*) __iota_close_brace $($rest:tt)*) => {
180        $crate::__iota_replace!(($v) (($($top)* {$($close)*}) $($stack)*) $($rest)*)
181    };
182
183    // Replace `iota` token with the intended expression.
184    (($v:expr) (($($top:tt)*) $($stack:tt)*) iota $($rest:tt)*) => {
185        $crate::__iota_replace!(($v) (($($top)* $v) $($stack)*) $($rest)*)
186    };
187
188    // Munch a token that is not `iota`.
189    (($v:expr) (($($top:tt)*) $($stack:tt)*) $first:tt $($rest:tt)*) => {
190        $crate::__iota_replace!(($v) (($($top)* $first) $($stack)*) $($rest)*)
191    };
192
193    // Done.
194    (($v:expr) (($($top:tt)+))) => {
195        $($top)+
196    };
197}