Skip to content

Commit

Permalink
Define Inst derive proc macro
Browse files Browse the repository at this point in the history
  • Loading branch information
Y-Nak committed Sep 9, 2024
1 parent 38d61d9 commit dac7644
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ members = [
"crates/parser",
"crates/filecheck",
"crates/triple",
"crates/macros",
"crates/interpreter",
]
19 changes: 19 additions & 0 deletions crates/macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "sonatina-macros"
version = "0.0.3-alpha"
edition = "2021"
authors = ["Sonatina Developers"]
license = "Apache-2.0"
readme = "../../README.md"
homepage = "https://github.com/fe-lang/sonatina/tree/main/crates/ir"
repository = "https://github.com/fe-lang/sonatina"
categories = ["compilers", "wasm"]
keywords = ["compiler", "evm", "wasm", "smart-contract"]

[lib]
proc_macro = true

[dependencies]
syn = { version = "1.0", features = ["full"] }
proc-macro2 = "1.0"
quote = "1.0"
129 changes: 129 additions & 0 deletions crates/macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
use quote::quote;

#[proc_macro_derive(Inst)]
pub fn derive_inst(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
let item_struct = syn::parse_macro_input!(item as syn::ItemStruct);

match InstBuilder::new(item_struct).and_then(InstBuilder::build) {
Ok(impls) => quote! {
#impls
}
.into(),

Err(e) => e.to_compile_error().into(),
}
}

struct InstBuilder {
struct_name: syn::Ident,
fields: syn::FieldsNamed,
}

impl InstBuilder {
fn new(item_struct: syn::ItemStruct) -> syn::Result<Self> {
let struct_ident = item_struct.ident;
let syn::Fields::Named(fields) = item_struct.fields else {
return Err(syn::Error::new_spanned(
item_struct.fields,
"tuple struct is not allowed for inst types",
));
};

if item_struct.generics.lt_token.is_some() {
Err(syn::Error::new_spanned(
item_struct.generics,
"generics is not allowed for inst types",
))
} else {
Ok(Self {
struct_name: struct_ident,
fields,
})
}
}

fn build(self) -> syn::Result<proc_macro2::TokenStream> {
let mut fields = Vec::with_capacity(self.fields.named.len());

for f in &self.fields.named {
if !matches!(f.vis, syn::Visibility::Inherited) {
return Err(syn::Error::new_spanned(
&f.vis,
"all members of inst types should be private",
));
};

fields.push((f.ident.clone().unwrap(), f.ty.clone()));
}

let ctor = self.make_ctor(&fields);
let accessors = self.make_accessors(&fields);
let cast_fn = self.make_cast_fn();

let struct_name = &self.struct_name;
Ok(quote! {
impl #struct_name {
#ctor

#accessors

#cast_fn
}
})
}

fn make_ctor(&self, fields: &[(syn::Ident, syn::Type)]) -> proc_macro2::TokenStream {
let ctor_args = fields.iter().map(|(ident, ty)| quote! {#ident: #ty});
let field_names = fields.iter().map(|(ident, _)| ident);
quote! {
pub fn new(hi: &dyn crate::HasInst<Self>, #(#ctor_args),*) -> Self {
Self {
#(#field_names: #field_names),*
}
}
}
}

fn make_cast_fn(&self) -> proc_macro2::TokenStream {
quote! {
pub fn cast<'i>(hi: &dyn crate::HasInst<Self>, inst: &'i dyn Inst) -> Option<&'i Self> {
if hi.is(inst) {
unsafe { Some(&*(inst as *const dyn Inst as *const Self)) }
} else {
None
}
}

pub fn cast_mut<'i>(
hi: &dyn HasInst<Self>,
inst: &'i mut dyn Inst,
) -> Option<&'i mut Self> {
if hi.is(inst) {
unsafe { Some(&mut *(inst as *mut dyn Inst as *mut Self)) }
} else {
None
}
}
}
}

fn make_accessors(&self, fields: &[(syn::Ident, syn::Type)]) -> proc_macro2::TokenStream {
let accessors = fields.iter().map(|(ident, ty)| {
let getter = quote::format_ident!("{ident}");
let get_mut = quote::format_ident!("{ident}_mut");
quote! {
pub fn #getter(&self) -> &#ty {
&self.#ident
}

pub fn #get_mut(&mut self) -> &mut #ty{
&mut self.#ident
}
}
});

quote! {
#(#accessors)*
}
}
}

0 comments on commit dac7644

Please sign in to comment.