extern crate proc_macro;
use proc_macro::{TokenStream};
use std::convert::TryInto;
use proc_macro2::Span;
use quote::{format_ident, quote};
use syn::{parse_macro_input, Expr, FieldValue, Member, Type, TypePath};
use syn::parse::Parser;
fn inner_place_expr(member: proc_macro2::TokenStream, val: &Expr, nesting: u32) -> proc_macro2::TokenStream {
match val {
Expr::Array(arr) => {
let construction = arr
.elems
.iter()
.enumerate()
.map(|(i, item)| {
inner_place_expr(quote! { #member[#i] }, item, nesting + 1)
});
quote! {
#(#construction)*
}
},
Expr::Repeat(rep) => {
let v = &rep.expr;
let len = &rep.len;
let loop_var = format_ident!("i_{}", nesting);
let initializer = inner_place_expr(quote! { #member[#loop_var] }, &v, nesting + 1);
quote! {
for #loop_var in 0..#len {
#initializer
}
}
},
expr => {
quote! {
(&raw mut #member).write(#expr);
}
}
}
}
struct PlaceKnownInput {
expr: syn::Expr,
ty: Option<syn::Type>,
}
struct PlaceIntoInput {
ptr: syn::Expr,
expr: syn::Expr,
}
impl syn::parse::Parse for PlaceKnownInput {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let expr = input.parse::<syn::Expr>()?;
let ty = if input.peek(syn::Token![,]) {
input.parse::<syn::Token![,]>()?;
Some(input.parse::<syn::Type>()?)
} else {
None
};
Ok(Self { expr, ty })
}
}
impl syn::parse::Parse for PlaceIntoInput {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let ptr = input.parse::<syn::Expr>()?;
let expr = if input.peek(syn::Token![,]) {
input.parse::<syn::Token![,]>()?;
input.parse::<syn::Expr>()?
} else {
return Err(syn::Error::new(Span::call_site(), "Expected expression to place into"))
};
Ok(Self { ptr, expr })
}
}
#[proc_macro]
pub fn place_boxed(input: TokenStream) -> TokenStream {
let inp = parse_macro_input!(input as PlaceKnownInput);
let ty = if let Some(ty) = inp.ty {
ty
} else {
let Expr::Struct(s) = &inp.expr else { return syn::Error::new_spanned(inp.expr, "Expected type argument for constructing non-structure type")
.to_compile_error().into() };
Type::Path(TypePath{
qself: None,
path: s.path.clone()
})
};
let expr = inp.expr;
quote! {
unsafe{
let mut res = std::boxed::Box::<#ty>::new_uninit();
let ptr = res.as_mut_ptr();
place_into!(ptr, #expr);
res.assume_init()
}
}.into()
}
#[proc_macro]
pub fn place_into(input: TokenStream) -> TokenStream {
let inp = parse_macro_input!(input as PlaceIntoInput);
let ptr = inp.ptr;
let struct_expr = match inp.expr {
syn::Expr::Struct(s) => s,
e => {
let generated = inner_place_expr(quote!{ (*ptr) }, &e, 0);
return quote! {
{
let _ensure_correct = || { #e };
let ptr = #ptr;
#generated
}
}.into()
}
};
if let Some(rest) = &struct_expr.rest {
return syn::Error::new_spanned(rest, "place_boxed limitation: Cannot use \
..Rest expressions in struct initializers")
.to_compile_error()
.into();
}
let mut generated = Vec::new();
for field in &struct_expr.fields {
let name = &field.member;
generated.push(
inner_place_expr(
quote!{
(*ptr).#name
},
&field.expr, 0
)
)
}
quote! {
{
let _ensure_struct_correct = || {
#struct_expr
};
let ptr = #ptr;
#(#generated)*
}
}.into()
}