2021-04-11 14:01:30 +08:00
|
|
|
extern crate proc_macro;
|
|
|
|
|
2021-04-11 17:08:41 +08:00
|
|
|
use syn::{Expr};
|
|
|
|
use syn::parse::{Parse, ParseStream, Result, Error};
|
|
|
|
use syn::punctuated::{Punctuated};
|
|
|
|
use syn::{parse_macro_input, Token};
|
|
|
|
use quote::{quote, format_ident};
|
|
|
|
use proc_macro::TokenStream;
|
|
|
|
|
|
|
|
struct Matrix {
|
|
|
|
// Represent the matrix as a row-major vector of vectors of expressions
|
|
|
|
rows: Vec<Vec<Expr>>,
|
|
|
|
ncols: usize,
|
2021-04-11 14:01:30 +08:00
|
|
|
}
|
|
|
|
|
2021-04-11 17:08:41 +08:00
|
|
|
impl Matrix {
|
|
|
|
fn nrows(&self) -> usize {
|
|
|
|
self.rows.len()
|
2021-04-11 14:01:30 +08:00
|
|
|
}
|
|
|
|
|
2021-04-11 17:08:41 +08:00
|
|
|
fn ncols(&self) -> usize {
|
|
|
|
self.ncols
|
2021-04-11 14:01:30 +08:00
|
|
|
}
|
|
|
|
|
2021-04-11 17:08:41 +08:00
|
|
|
fn to_col_major_repr(&self) -> Vec<Expr> {
|
|
|
|
let mut data = Vec::with_capacity(self.nrows() * self.ncols());
|
|
|
|
for j in 0 .. self.ncols() {
|
|
|
|
for i in 0 .. self.nrows() {
|
|
|
|
data.push(self.rows[i][j].clone());
|
|
|
|
}
|
2021-04-11 14:01:30 +08:00
|
|
|
}
|
2021-04-11 17:08:41 +08:00
|
|
|
data
|
2021-04-11 14:01:30 +08:00
|
|
|
}
|
2021-04-11 17:08:41 +08:00
|
|
|
}
|
2021-04-11 14:01:30 +08:00
|
|
|
|
2021-04-11 17:08:41 +08:00
|
|
|
type MatrixRowSyntax = Punctuated<Expr, Token![,]>;
|
2021-04-11 14:01:30 +08:00
|
|
|
|
2021-04-11 17:08:41 +08:00
|
|
|
impl Parse for Matrix {
|
|
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
|
|
let mut rows = Vec::new();
|
2021-04-11 23:29:33 +08:00
|
|
|
let mut ncols = None;
|
|
|
|
|
|
|
|
while !input.is_empty() {
|
|
|
|
let row_span = input.span();
|
|
|
|
let row = MatrixRowSyntax::parse_separated_nonempty(input)?;
|
2021-04-11 14:01:30 +08:00
|
|
|
|
2021-04-11 23:29:33 +08:00
|
|
|
if let Some(ncols) = ncols {
|
|
|
|
if row.len() != ncols {
|
|
|
|
let row_idx = rows.len();
|
|
|
|
let error_msg = format!(
|
|
|
|
"Unexpected number of entries in row {}. Expected {}, found {} entries.",
|
|
|
|
row_idx,
|
|
|
|
ncols,
|
|
|
|
row.len());
|
|
|
|
return Err(Error::new(row_span, error_msg));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ncols = Some(row.len());
|
2021-04-11 14:01:30 +08:00
|
|
|
}
|
2021-04-11 17:08:41 +08:00
|
|
|
rows.push(row.into_iter().collect());
|
2021-04-11 23:29:33 +08:00
|
|
|
|
|
|
|
// We've just read a row, so if there are more tokens, there must be a semi-colon,
|
|
|
|
// otherwise the input is malformed
|
|
|
|
if !input.is_empty() {
|
|
|
|
input.parse::<Token![;]>()?;
|
|
|
|
}
|
2021-04-11 14:01:30 +08:00
|
|
|
}
|
|
|
|
|
2021-04-11 17:08:41 +08:00
|
|
|
Ok(Self {
|
|
|
|
rows,
|
2021-04-11 23:29:33 +08:00
|
|
|
ncols: ncols.unwrap_or(0)
|
2021-04-11 17:08:41 +08:00
|
|
|
})
|
2021-04-11 14:01:30 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[proc_macro]
|
|
|
|
pub fn matrix(stream: TokenStream) -> TokenStream {
|
2021-04-11 17:08:41 +08:00
|
|
|
let matrix = parse_macro_input!(stream as Matrix);
|
|
|
|
|
2021-04-14 23:30:52 +08:00
|
|
|
let row_dim = matrix.nrows();
|
|
|
|
let col_dim = matrix.ncols();
|
2021-04-11 17:08:41 +08:00
|
|
|
let entries_col_major = matrix.to_col_major_repr();
|
|
|
|
|
|
|
|
// TODO: Use quote_spanned instead??
|
|
|
|
// TODO: Construct directly from array?
|
|
|
|
let output = quote! {
|
2021-04-14 23:30:52 +08:00
|
|
|
nalgebra::SMatrix::<_, #row_dim, #col_dim>
|
2021-04-11 17:08:41 +08:00
|
|
|
::from_column_slice(&[#(#entries_col_major),*])
|
|
|
|
};
|
2021-04-11 14:01:30 +08:00
|
|
|
|
2021-04-11 17:08:41 +08:00
|
|
|
proc_macro::TokenStream::from(output)
|
2021-04-11 14:01:30 +08:00
|
|
|
}
|