bit_seq/lib.rs
1//! `bit_seq` provides procedural macros for generating bit sequences.
2//!
3//! # Overview
4//!
5//! This crate provides the macro [`bseq!`](bseq!), which allows for the creation of bit sequences
6//! using a simple and intuitive syntax. Bit sequences can be created from raw binary values,
7//! hexadecimal values, or even variable expressions. This makes the `bit_seq` crate a useful tool for
8//! systems programming, hardware interfacing, or any application where bit manipulation is common.
9//!
10//! `bit_seq` also provides [`bseq_8!`](bseq_8!), [`bseq_16!`](bseq_16!), [`bseq_32!`](bseq_32!), [`bseq_64!`](bseq_64!) and [`bseq_128!`](bseq_128!) to
11//! simply type mixing.
12//!
13//! # Examples
14//!
15//! The following examples illustrate some of the ways `bseq!` can be used.
16//!
17//! ## Raw Bit Sequences
18//!
19//! ```
20//! use bit_seq::bseq;
21//!
22//! let t = bseq!(0110 01 0 1);
23//! assert_eq!(t, 0b0110_01_0_1);
24//! ```
25//!
26//! ## Hex Values
27//!
28//! Hexadecimal values are interpreted as 4-bit sequences.
29//!
30//! ```
31//! use bit_seq::bseq;
32//!
33//! let t = bseq!(01 0x1f);
34//! assert_eq!(t, 0b01_0001_1111);
35//! ```
36//!
37//! ## Length Expressions
38//!
39//! Length expressions take the form `<val>:<len>`, where `<len>` is the number of bits from `<val>` to be used.
40//!
41//! ```
42//! use bit_seq::bseq;
43//!
44//! let t = bseq!(3:1 0 0xf:2);
45//! assert_eq!(t, 0b1_0_11);
46//! ```
47//!
48//! ## Variable Interpolation
49//!
50//! Variable interpolation is supported for length expressions.
51//!
52//! ```
53//! use bit_seq::bseq;
54//! let var = 0xf;
55//! let t = bseq!(10 var:2);
56//! assert_eq!(t, 0b10_11);
57//! ```
58//!
59//! ## Unary Operations
60//!
61//! The bseq syntax supports unary operations for length expressions. This simplifies bit sequences like
62//! `0b111111`.
63//!
64//! ```
65//! use bit_seq::bseq;
66//! // bit negation
67//! assert_eq!(bseq!(!0:6), 0b111111);
68//!
69//! // numerical negation with variable interpolation
70//! let var = 1;
71//! assert_eq!(bseq!(-var:8), 0xff);
72//! ```
73//!
74//! # Performance
75//!
76//! The `bseq!` macro compiles down to standard bit manipulation operations, meaning there is no runtime overhead to using it.
77
78
79use proc_macro::TokenStream;
80
81use proc_macro_error::*;
82use quote::{quote, quote_spanned};
83use syn::{LitInt, parse_macro_input, parse_quote, Type};
84use syn::__private::TokenStream2;
85use syn::spanned::Spanned;
86
87use crate::bit_seq_input::{BitSegment::{self, *}, BitSeqInput};
88
89mod bit_seq_input;
90
91
92/// `bseq` is a procedural macro for creating bit sequences.
93///
94/// This macro enables the generation of bit sequences using a simple syntax.
95/// Bit sequences can be specified directly, through hex values, or by using identifiers or integers
96/// each with a specific length. This proves especially useful in systems programming and when interacting
97/// with low-level hardware or protocols where bit manipulation is a common requirement.
98///
99/// # Examples
100///
101/// #### Direct raw bit sequence:
102/// ```
103/// use bit_seq::bseq;
104///
105/// let t = bseq!(0110 01 0 1);
106/// assert_eq!(t, 0b0110_01_0_1);
107/// ```
108///
109/// #### Using hex values:
110///
111/// Hex values conveniently add 4 bits per hexadecimal place.
112/// ```
113/// use bit_seq::bseq;
114///
115/// let t = bseq!(01 0x1f);
116/// assert_eq!(t, 0b01_0001_1111);
117/// ```
118///
119/// #### Using value length expression:
120///
121/// Employ the format `<val>:<len>` where `len` specifies how many of the
122/// least significant bits from `val` should be used.
123/// ```
124/// use bit_seq::bseq;
125///
126/// let t = bseq!(3:1 0 0xf:2);
127/// assert_eq!(t, 0b1_0_11);
128/// ```
129///
130/// #### Using variable length expression:
131///
132/// It is also possible to interpolate outer variables for length expressions.
133/// ```
134/// use bit_seq::bseq;
135/// let var = 0xf;
136/// let t = bseq!(10 var:2);
137/// assert_eq!(t, 0b10_11);
138/// ```
139///
140/// ## Unary Operations
141///
142/// The bseq syntax supports unary operations for length expressions. This simplifies bit sequences like
143/// `0b111111`.
144///
145/// ```
146/// use bit_seq::bseq;
147/// // bit negation
148/// assert_eq!(bseq!(!0:6), 0b111111);
149///
150/// // numerical negation with variable interpolation
151/// let var = 1;
152/// assert_eq!(bseq!(-var:8), 0xff);
153/// ```
154///
155/// Note: Since the macros are compiled into common bit manipulation operations,
156/// the usage of this macro doesn't introduce additional runtime overhead.
157///
158/// The macro outputs a numerical value with the appropriate bits set, providing an
159/// efficient method to generate specific bit sequences.
160#[proc_macro]
161#[proc_macro_error]
162pub fn bseq(input: TokenStream) -> TokenStream {
163 process(input, None)
164}
165
166/// The `bseq_8` procedural macro is specifically tailored for creating 8-bit sequences.
167///
168/// It is primarily utilized when there's a need to accommodate variable types different from those
169/// provided by the macro or when working with variable-length expressions involving mixed types.
170///
171/// For instance, the following example would fail to compile due to a type mismatch:
172/// ```compile_fail
173/// use bit_seq::bseq;
174/// let foo: u32 = 4;
175/// let bar: u64 = 2;
176/// let t: u8 = bseq!(foo:5 bar:3);
177/// ```
178///
179/// The `bseq_8` macro addresses such scenarios, as demonstrated below:
180/// ```
181/// use bit_seq::bseq_8;
182/// let foo: u32 = 4;
183/// let bar: u64 = 2;
184/// let t: u8 = bseq_8!(foo:5 bar:3);
185/// ```
186///
187/// It is important to note that `bseq_8` effectively performs as `bseq!(...)`, albeit with intermediate type casts.
188/// For a comprehensive understanding on the usage of `bseq_8`, please refer to the [`bseq!`](bseq!) documentation.
189#[proc_macro]
190pub fn bseq_8(input: TokenStream) -> TokenStream {
191 let ty: Type = parse_quote!(u8);
192 process(input, Some(ty))
193}
194
195/// The `bseq_16` procedural macro is specifically tailored for creating 16-bit sequences.
196///
197/// It is primarily utilized when there's a need to accommodate variable types different from those
198/// provided by the macro or when working with variable-length expressions involving mixed types.
199///
200/// For instance, the following example would fail to compile due to a type mismatch:
201/// ```compile_fail
202/// use bit_seq::bseq;
203/// let foo: u32 = 4;
204/// let bar: u64 = 2;
205/// let t: u16 = bseq!(foo:5 bar:11);
206/// ```
207///
208/// The `bseq_16` macro addresses such scenarios, as demonstrated below:
209/// ```
210/// use bit_seq::bseq_16;
211/// let foo: u32 = 4;
212/// let bar: u64 = 2;
213/// let t: u16 = bseq_16!(foo:5 bar:11);
214/// ```
215///
216/// It is important to note that `bseq_16` effectively performs as `bseq!(...)`, albeit with intermediate type casts.
217/// For a comprehensive understanding on the usage of `bseq_16`, please refer to the [`bseq!`](bseq!) documentation.
218#[proc_macro]
219pub fn bseq_16(input: TokenStream) -> TokenStream {
220 let ty: Type = parse_quote!(u16);
221 process(input, Some(ty))
222}
223
224/// The `bseq_32` procedural macro is specifically tailored for creating 32-bit sequences.
225///
226/// It is primarily utilized when there's a need to accommodate variable types different from those
227/// provided by the macro or when working with variable-length expressions involving mixed types.
228///
229/// For instance, the following example would fail to compile due to a type mismatch:
230/// ```compile_fail
231/// use bit_seq::bseq;
232/// let foo: u8 = 4;
233/// let bar: u64 = 2;
234/// let t: u32 = bseq!(foo:5 bar:27);
235/// ```
236///
237/// The `bseq_32` macro addresses such scenarios, as demonstrated below:
238/// ```
239/// use bit_seq::bseq_32;
240/// let foo: u8 = 4;
241/// let bar: u64 = 2;
242/// let t: u32 = bseq_32!(foo:5 bar:27);
243/// ```
244///
245/// It is important to note that `bseq_32` effectively performs as `bseq!(...)`, albeit with intermediate type casts.
246/// For a comprehensive understanding on the usage of `bseq_32`, please refer to the [`bseq!`](bseq!) documentation.
247#[proc_macro]
248pub fn bseq_32(input: TokenStream) -> TokenStream {
249 let ty: Type = parse_quote!(u32);
250 process(input, Some(ty))
251}
252
253/// The `bseq_64` procedural macro is designed for creating 64-bit sequences.
254///
255/// It is primarily utilized when dealing with variable types that are different from those
256/// provided by the macro or when working with variable-length expressions that involve mixed types.
257///
258/// The following example won't compile due to a type mismatch:
259/// ```compile_fail
260/// use bit_seq::bseq;
261/// let foo: u32 = 4;
262/// let bar: u16 = 2;
263/// let t: u64 = bseq!(foo:5 bar:59);
264/// ```
265///
266/// The `bseq_64` macro provides a solution for such cases:
267/// ```
268/// use bit_seq::bseq_64;
269/// let foo: u32 = 4;
270/// let bar: u16 = 2;
271/// let t: u64 = bseq_64!(foo:5 bar:59);
272/// ```
273///
274/// Note that `bseq_64` is essentially `bseq!(...)` with intermediate type casts. For details on how to use `bseq_64`,
275/// please refer to the [`bseq!`](bseq!) documentation.
276#[proc_macro]
277pub fn bseq_64(input: TokenStream) -> TokenStream {
278 let ty: Type = parse_quote!(u64);
279 process(input, Some(ty))
280}
281
282/// The `bseq_128` procedural macro is designed for creating 128-bit sequences.
283///
284/// It is primarily utilized when dealing with variable types that are different from those
285/// provided by the macro or when working with variable-length expressions that involve mixed types.
286///
287/// The following example won't compile due to a type mismatch:
288/// ```compile_fail
289/// use bit_seq::bseq;
290/// let foo: u32 = 4;
291/// let bar: u64 = 2;
292/// let t: u128 = bseq!(foo:5 bar:59);
293/// ```
294///
295/// The `bseq_128` macro provides a solution for such cases:
296/// ```
297/// use bit_seq::bseq_128;
298/// let foo: u32 = 4;
299/// let bar: u64 = 2;
300/// let t: u128 = bseq_128!(foo:5 bar:59);
301/// ```
302///
303/// Note that `bseq_128` is essentially `bseq!(...)` with intermediate type casts. For details on how to use `bseq_128`,
304/// please refer to the [`bseq!`](bseq!) documentation.
305#[proc_macro]
306pub fn bseq_128(input: TokenStream) -> TokenStream {
307 let ty: Type = parse_quote!(u128);
308 process(input, Some(ty))
309}
310
311/// Processes the `bseq` input stream with a specified variable type.
312///
313/// `bseq!` has variable type None \
314/// `bseq8!` has variable type Option<Type<u8>> \
315/// ...
316fn process(input: TokenStream, var_type: Option<Type>) -> TokenStream {
317 // parse input
318 let input = parse_macro_input!(input as BitSeqInput);
319
320 // construct shift token streams
321 let mut bit_len = 0;
322 let shifts: Vec<_> = input.segments()
323 .iter().rev()
324 .map(|seg| map_segment(seg, &mut bit_len, &var_type))
325 .collect();
326
327 // combine all shift segments
328 let span = proc_macro2::Span::call_site();
329
330 let mut macro_out = if let Some(ty) = var_type {
331 quote!((#(#shifts)|*) as #ty)
332 } else {
333 quote!(#(#shifts)|*)
334 };
335
336 if macro_out.is_empty() {
337 // if no input provided, result is 0
338 macro_out = quote_spanned!(span=> 0);
339 }
340
341 macro_out.into()
342}
343
344
345fn map_segment(seg: &BitSegment, curr_bit_len: &mut usize, expr_type: &Option<Type>) -> TokenStream2 {
346 let (val, len) = match seg {
347 Bits(bits) => {
348 let b = bits.to_string();
349 let num = usize::from_str_radix(&b, 2).unwrap();
350 let num_lit = LitInt::new(&num.to_string(), b.span());
351 let span = bits.span();
352 let rep = quote_spanned!(span=> #num_lit);
353 (rep, b.len())
354 }
355 Expr(expr, len_lit) => {
356 let len: usize = len_lit.base10_parse().unwrap_or_else(|_| abort!(len_lit, "Couldn't be parsed!"));
357 let mask: u128 = (1 << len) - 1;
358 let mask_lit = LitInt::new(&mask.to_string(), expr.span());
359 let span = expr.span();
360
361 let rep = if let Some(ty) = expr_type {
362 quote_spanned!(span=> (#expr as #ty) & #mask_lit)
363 } else {
364 quote_spanned!(span=> #expr & #mask_lit)
365 };
366
367 (rep, len)
368 }
369 };
370
371 let span = val.span();
372 let bit_len_lit = LitInt::new(&curr_bit_len.to_string(), span);
373 let res = quote_spanned!(span=> (#val) << #bit_len_lit);
374 *curr_bit_len += len;
375 res
376}