nalgebra_macros/
lib.rs

1//! Macros for `nalgebra`.
2//!
3//! This crate is not intended for direct consumption. Instead, the macros are re-exported by
4//! `nalgebra` if the `macros` feature is enabled (enabled by default).
5
6extern crate proc_macro;
7
8use proc_macro::TokenStream;
9use quote::{quote, ToTokens, TokenStreamExt};
10use syn::parse::{Error, Parse, ParseStream, Result};
11use syn::punctuated::Punctuated;
12use syn::Expr;
13use syn::{parse_macro_input, Token};
14
15use proc_macro2::{Delimiter, Spacing, TokenStream as TokenStream2, TokenTree};
16use proc_macro2::{Group, Punct};
17
18struct Matrix {
19    // Represent the matrix as a row-major vector of vectors of expressions
20    rows: Vec<Vec<Expr>>,
21    ncols: usize,
22}
23
24impl Matrix {
25    fn nrows(&self) -> usize {
26        self.rows.len()
27    }
28
29    fn ncols(&self) -> usize {
30        self.ncols
31    }
32
33    /// Produces a stream of tokens representing this matrix as a column-major nested array.
34    fn to_col_major_nested_array_tokens(&self) -> TokenStream2 {
35        let mut result = TokenStream2::new();
36        for j in 0..self.ncols() {
37            let mut col = TokenStream2::new();
38            let col_iter = (0..self.nrows()).map(move |i| &self.rows[i][j]);
39            col.append_separated(col_iter, Punct::new(',', Spacing::Alone));
40            result.append(Group::new(Delimiter::Bracket, col));
41            result.append(Punct::new(',', Spacing::Alone));
42        }
43        TokenStream2::from(TokenTree::Group(Group::new(Delimiter::Bracket, result)))
44    }
45
46    /// Produces a stream of tokens representing this matrix as a column-major flat array
47    /// (suitable for representing e.g. a `DMatrix`).
48    fn to_col_major_flat_array_tokens(&self) -> TokenStream2 {
49        let mut data = TokenStream2::new();
50        for j in 0..self.ncols() {
51            for i in 0..self.nrows() {
52                self.rows[i][j].to_tokens(&mut data);
53                data.append(Punct::new(',', Spacing::Alone));
54            }
55        }
56        TokenStream2::from(TokenTree::Group(Group::new(Delimiter::Bracket, data)))
57    }
58}
59
60type MatrixRowSyntax = Punctuated<Expr, Token![,]>;
61
62impl Parse for Matrix {
63    fn parse(input: ParseStream) -> Result<Self> {
64        let mut rows = Vec::new();
65        let mut ncols = None;
66
67        while !input.is_empty() {
68            let row_span = input.span();
69            let row = MatrixRowSyntax::parse_separated_nonempty(input)?;
70
71            if let Some(ncols) = ncols {
72                if row.len() != ncols {
73                    let row_idx = rows.len();
74                    let error_msg = format!(
75                        "Unexpected number of entries in row {}. Expected {}, found {} entries.",
76                        row_idx,
77                        ncols,
78                        row.len()
79                    );
80                    return Err(Error::new(row_span, error_msg));
81                }
82            } else {
83                ncols = Some(row.len());
84            }
85            rows.push(row.into_iter().collect());
86
87            // We've just read a row, so if there are more tokens, there must be a semi-colon,
88            // otherwise the input is malformed
89            if !input.is_empty() {
90                input.parse::<Token![;]>()?;
91            }
92        }
93
94        Ok(Self {
95            rows,
96            ncols: ncols.unwrap_or(0),
97        })
98    }
99}
100
101/// Construct a fixed-size matrix directly from data.
102///
103/// **Note: Requires the `macro` feature to be enabled (enabled by default)**.
104///
105/// This macro facilitates easy construction of matrices when the entries of the matrix are known
106/// (either as constants or expressions). This macro produces an instance of `SMatrix`. This means
107/// that the data of the matrix is stored on the stack, and its dimensions are fixed at
108/// compile-time. If you want to construct a dynamic matrix, use [`dmatrix!`] instead.
109///
110/// `matrix!` is intended to be both the simplest and most efficient way to construct (small)
111/// matrices, and can also be used in *const fn* contexts.
112///
113/// The syntax is MATLAB-like. Column elements are separated by a comma (`,`), and a semi-colon
114/// (`;`) designates that a new row begins.
115///
116/// # Examples
117///
118/// ```
119/// use nalgebra::matrix;
120///
121/// // Produces a Matrix3<_> == SMatrix<_, 3, 3>
122/// let a = matrix![1, 2, 3;
123///                 4, 5, 6;
124///                 7, 8, 9];
125/// ```
126///
127/// You can construct matrices with arbitrary expressions for its elements:
128///
129/// ```
130/// use nalgebra::{matrix, Matrix2};
131/// let theta = 0.45f64;
132///
133/// let r = matrix![theta.cos(), - theta.sin();
134///                 theta.sin(),   theta.cos()];
135/// ```
136#[proc_macro]
137pub fn matrix(stream: TokenStream) -> TokenStream {
138    let matrix = parse_macro_input!(stream as Matrix);
139
140    let row_dim = matrix.nrows();
141    let col_dim = matrix.ncols();
142
143    let array_tokens = matrix.to_col_major_nested_array_tokens();
144
145    //  TODO: Use quote_spanned instead??
146    let output = quote! {
147        nalgebra::SMatrix::<_, #row_dim, #col_dim>
148            ::from_array_storage(nalgebra::ArrayStorage(#array_tokens))
149    };
150
151    proc_macro::TokenStream::from(output)
152}
153
154/// Construct a dynamic matrix directly from data.
155///
156/// **Note: Requires the `macro` feature to be enabled (enabled by default)**.
157///
158/// The syntax is exactly the same as for [`matrix!`], but instead of producing instances of
159/// `SMatrix`, it produces instances of `DMatrix`. At the moment it is not usable
160/// in `const fn` contexts.
161///
162/// ```
163/// use nalgebra::dmatrix;
164///
165/// // Produces a DMatrix<_>
166/// let a = dmatrix![1, 2, 3;
167///                  4, 5, 6;
168///                  7, 8, 9];
169/// ```
170#[proc_macro]
171pub fn dmatrix(stream: TokenStream) -> TokenStream {
172    let matrix = parse_macro_input!(stream as Matrix);
173
174    let row_dim = matrix.nrows();
175    let col_dim = matrix.ncols();
176
177    let array_tokens = matrix.to_col_major_flat_array_tokens();
178
179    //  TODO: Use quote_spanned instead??
180    let output = quote! {
181        nalgebra::DMatrix::<_>
182            ::from_vec_storage(nalgebra::VecStorage::new(
183                nalgebra::Dynamic::new(#row_dim),
184                nalgebra::Dynamic::new(#col_dim),
185                vec!#array_tokens))
186    };
187
188    proc_macro::TokenStream::from(output)
189}
190
191struct Vector {
192    elements: Vec<Expr>,
193}
194
195impl Vector {
196    fn to_array_tokens(&self) -> TokenStream2 {
197        let mut data = TokenStream2::new();
198        data.append_separated(&self.elements, Punct::new(',', Spacing::Alone));
199        TokenStream2::from(TokenTree::Group(Group::new(Delimiter::Bracket, data)))
200    }
201
202    fn len(&self) -> usize {
203        self.elements.len()
204    }
205}
206
207impl Parse for Vector {
208    fn parse(input: ParseStream) -> Result<Self> {
209        // The syntax of a vector is just the syntax of a single matrix row
210        if input.is_empty() {
211            Ok(Self {
212                elements: Vec::new(),
213            })
214        } else {
215            let elements = MatrixRowSyntax::parse_separated_nonempty(input)?
216                .into_iter()
217                .collect();
218            Ok(Self { elements })
219        }
220    }
221}
222
223/// Construct a fixed-size column vector directly from data.
224///
225/// **Note: Requires the `macro` feature to be enabled (enabled by default)**.
226///
227/// Similarly to [`matrix!`], this macro facilitates easy construction of fixed-size vectors.
228/// However, whereas the [`matrix!`] macro expects each row to be separated by a semi-colon,
229/// the syntax of this macro is instead similar to `vec!`, in that the elements of the vector
230/// are simply listed consecutively.
231///
232/// `vector!` is intended to be the most readable and performant way of constructing small,
233/// fixed-size vectors, and it is usable in `const fn` contexts.
234///
235/// ## Examples
236///
237/// ```
238/// use nalgebra::vector;
239///
240/// // Produces a Vector3<_> == SVector<_, 3>
241/// let v = vector![1, 2, 3];
242/// ```
243#[proc_macro]
244pub fn vector(stream: TokenStream) -> TokenStream {
245    let vector = parse_macro_input!(stream as Vector);
246    let len = vector.len();
247    let array_tokens = vector.to_array_tokens();
248    let output = quote! {
249        nalgebra::SVector::<_, #len>
250            ::from_array_storage(nalgebra::ArrayStorage([#array_tokens]))
251    };
252    proc_macro::TokenStream::from(output)
253}
254
255/// Construct a dynamic column vector directly from data.
256///
257/// **Note: Requires the `macro` feature to be enabled (enabled by default)**.
258///
259/// The syntax is exactly the same as for [`vector!`], but instead of producing instances of
260/// `SVector`, it produces instances of `DVector`. At the moment it is not usable
261/// in `const fn` contexts.
262///
263/// ```
264/// use nalgebra::dvector;
265///
266/// // Produces a DVector<_>
267/// let v = dvector![1, 2, 3];
268/// ```
269#[proc_macro]
270pub fn dvector(stream: TokenStream) -> TokenStream {
271    let vector = parse_macro_input!(stream as Vector);
272    let len = vector.len();
273    let array_tokens = vector.to_array_tokens();
274    let output = quote! {
275        nalgebra::DVector::<_>
276            ::from_vec_storage(nalgebra::VecStorage::new(
277                nalgebra::Dynamic::new(#len),
278                nalgebra::Const::<1>,
279                vec!#array_tokens))
280    };
281    proc_macro::TokenStream::from(output)
282}
283
284/// Construct a fixed-size point directly from data.
285///
286/// **Note: Requires the `macro` feature to be enabled (enabled by default)**.
287///
288/// Similarly to [`vector!`], this macro facilitates easy construction of points.
289///
290/// `point!` is intended to be the most readable and performant way of constructing small,
291/// points, and it is usable in `const fn` contexts.
292///
293/// ## Examples
294///
295/// ```
296/// use nalgebra::point;
297///
298/// // Produces a Point3<_>
299/// let v = point![1, 2, 3];
300/// ```
301#[proc_macro]
302pub fn point(stream: TokenStream) -> TokenStream {
303    let vector = parse_macro_input!(stream as Vector);
304    let len = vector.len();
305    let array_tokens = vector.to_array_tokens();
306    let output = quote! {
307        nalgebra::Point::<_, #len> {
308            coords: nalgebra::SVector::<_, #len>
309                        ::from_array_storage(nalgebra::ArrayStorage([#array_tokens]))
310        }
311    };
312    proc_macro::TokenStream::from(output)
313}