diff --git a/Cargo.toml b/Cargo.toml index 45fdb17a..ad24b809 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -92,7 +92,7 @@ matrixcompare = "0.2.0" itertools = "0.10" [workspace] -members = [ "nalgebra-lapack", "nalgebra-glm", "nalgebra-sparse" ] +members = [ "nalgebra-lapack", "nalgebra-glm", "nalgebra-sparse", "nalgebra-macros" ] resolver = "2" [[example]] diff --git a/nalgebra-macros/Cargo.toml b/nalgebra-macros/Cargo.toml new file mode 100644 index 00000000..74b5d1b2 --- /dev/null +++ b/nalgebra-macros/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "nalgebra-macros" +version = "0.1.0" +authors = ["Andreas Longva "] +edition = "2018" + +[lib] +proc-macro = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +syn = "1.0" + +[dev-dependencies] +nalgebra = { version = "0.25.4", path = ".." } diff --git a/nalgebra-macros/src/lib.rs b/nalgebra-macros/src/lib.rs new file mode 100644 index 00000000..b4e2d4b2 --- /dev/null +++ b/nalgebra-macros/src/lib.rs @@ -0,0 +1,99 @@ +extern crate proc_macro; + +use proc_macro::{TokenStream, TokenTree, Literal, Ident, Punct, Spacing, Group, Delimiter}; +use std::iter::FromIterator; + +struct MatrixEntries { + entries: Vec> +} + +impl MatrixEntries { + fn new() -> Self { + Self { + entries: Vec::new() + } + } + + fn begin_new_row(&mut self) { + self.entries.push(Vec::new()); + } + + fn push_entry(&mut self, entry: TokenTree) { + if self.entries.is_empty() { + self.entries.push(Vec::new()); + } + let mut last_row = self.entries.last_mut().unwrap(); + last_row.push(entry); + } + + fn build_stream(&self) -> TokenStream { + let num_rows = self.entries.len(); + let num_cols = self.entries.first() + .map(|first_row| first_row.len()) + .unwrap_or(0); + + // First check that dimensions are consistent + for (i, row) in self.entries.iter().enumerate() { + if row.len() != num_cols { + panic!("Unexpected number of columns in row {}: {}. Expected {}", i, row.len(), num_cols); + } + } + + let mut array_tokens = Vec::new(); + + // Collect entries in column major order + for i in 0 .. num_rows { + for j in 0 .. num_cols { + let entry = &self.entries[i][j]; + array_tokens.push(entry.clone()); + array_tokens.push(TokenTree::Punct(Punct::new(',', Spacing::Alone))); + } + } + + let row_dim = format!("U{}", num_rows); + let col_dim = format!("U{}", num_cols); + // let imports = format!("use nalgebra::\{Matrix, {}, {}\};", row_dim, col_dim); + // let constructor = format!("Matrix::<_, {}, {}>::from_slice", row_dim, col_dim); + // let array_group = Group::new(Delimiter::Bracket, TokenStream::from_iter(array_tokens.into_iter())); + + let array_stream = TokenStream::from_iter(array_tokens); + + // TODO: Build this up without parsing? + format!(r"{{ + nalgebra::MatrixMN::<_, nalgebra::{row_dim}, nalgebra::{col_dim}>::from_column_slice(&[ + {array_tokens} + ]) + }}", row_dim=row_dim, col_dim=col_dim, array_tokens=array_stream.to_string()).parse().unwrap() + + + // let mut outer_group = Group::new(Delimiter::Brace, + // + // ); + + + // TODO: Outer group + + + + + // todo!() + } +} + +#[proc_macro] +pub fn matrix(stream: TokenStream) -> TokenStream { + let mut entries = MatrixEntries::new(); + for tree in stream { + match tree { + // TokenTree::Ident(ident) => entries.push_entry(tree), + // TokenTree::Literal(literal) => entries.push_entry(tree), + TokenTree::Punct(punct) if punct == ';' => entries.begin_new_row(), + TokenTree::Punct(punct) if punct == ',' => {}, + // TokenTree::Punct(punct) => panic!("Unexpected punctuation: '{}'", punct), + // TokenTree::Group(_) => panic!("Unexpected token group"), + _ => entries.push_entry(tree) + } + } + + entries.build_stream() +} \ No newline at end of file diff --git a/nalgebra-macros/tests/tests.rs b/nalgebra-macros/tests/tests.rs new file mode 100644 index 00000000..5763e139 --- /dev/null +++ b/nalgebra-macros/tests/tests.rs @@ -0,0 +1,6 @@ +use nalgebra_macros::matrix; + +#[test] +fn basic_usage() { + matrix![ 1, 3; 4, 5*3]; +} \ No newline at end of file