diff options
Diffstat (limited to 'crates/elfmacros/src')
| -rw-r--r-- | crates/elfmacros/src/lib.rs | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/crates/elfmacros/src/lib.rs b/crates/elfmacros/src/lib.rs index e69de29..e7f05a2 100644 --- a/crates/elfmacros/src/lib.rs +++ b/crates/elfmacros/src/lib.rs @@ -0,0 +1,182 @@ +use proc_macro2::TokenStream; +use quote::{quote, TokenStreamExt}; + +fn title_to_kebab_case(s: &str) -> String { + let mut ret = String::new(); + let mut start = 0; + for (end, _) in s.match_indices(|c: char| c.is_ascii_uppercase()) { + if start != end { + ret.push_str(&s[start..end].to_ascii_lowercase()); + ret.push_str("-"); + } + start = end; + } + ret.push_str(&s[start..].to_ascii_lowercase()); + // s.split_inclusive(|c: char| c.is_ascii_uppercase()) + // .map(|s| s.to_ascii_lowercase()) + // .join("-") + ret +} + +#[proc_macro_derive(Unifiable)] +pub fn derive_unifiable(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = syn::parse_macro_input!(input as syn::DeriveInput); + let name = input.ident; + let en = if let syn::Data::Enum(e) = &input.data { e } else { + return proc_macro::TokenStream::from(quote! { compile_error!("can only derive unifiable for an enum"); }) + }; + let mut cases = TokenStream::new(); + for v in en.variants.iter() { + let vnm = &v.ident; + cases.append_all( + quote! { + (Self :: #vnm (a), Self::Concrete:: #vnm (b) ) => a.unify(b, u), + }, + ); + } + let expanded = quote! { + impl crate::knowledge::Unifiable for #name { + fn unify(&self, other: &Self::Concrete, u: crate::knowledge::Unification) -> Option<crate::knowledge::Unification> { + match (self, other) { + #cases + _ => None, + } + } + } + }; + proc_macro::TokenStream::from(expanded) +} + +#[proc_macro_derive(Substitutable)] +pub fn derive_substitutable(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = syn::parse_macro_input!(input as syn::DeriveInput); + let name = input.ident; + let en = if let syn::Data::Enum(e) = &input.data { e } else { + return proc_macro::TokenStream::from(quote! { compile_error!("can only derive substitutable for an enum"); }) + }; + let mut subcases = TokenStream::new(); + let mut mvcases = TokenStream::new(); + for v in en.variants.iter() { + let vnm = &v.ident; + subcases.append_all( + quote! { + Self :: #vnm (x) => Self :: #vnm (x.substitute(u)), + }, + ); + mvcases.append_all( + quote! { + Self :: #vnm (x) => Self :: #vnm (x.map_variables(f)), + }, + ); + } + let expanded = quote! { + impl crate::knowledge::Substitutable for #name { + fn substitute(&self, u: &crate::knowledge::Unification) -> Self { + match self { + #subcases + } + } + fn map_variables<F>(&self, f: &mut F) -> Self where F: FnMut(crate::knowledge::Var) -> Var { + match self { + #mvcases + } + } + } + }; + proc_macro::TokenStream::from(expanded) +} + +#[proc_macro_derive(Concrete)] +pub fn derive_concrete(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = syn::parse_macro_input!(input as syn::DeriveInput); + let name = input.ident; + let en = if let syn::Data::Enum(e) = &input.data { e } else { + return proc_macro::TokenStream::from(quote! { compile_error!("can only derive concretizable for an enum"); }) + }; + let hnm = syn::Ident::new(&format!("{}Hole", name), name.span()); + let dnm = syn::Ident::new(&format!("{}Discriminants", name), name.span()); + let mut cases = TokenStream::new(); + let mut dcases = TokenStream::new(); + let mut dispcases = TokenStream::new(); + let mut variants = TokenStream::new(); + for v in en.variants.iter() { + let vnm = &v.ident; + let kebab = title_to_kebab_case(&vnm.to_string()); + let mut vtypes = TokenStream::new(); + let mut tuple = false; + if let syn::Fields::Unnamed(fu) = &v.fields { + for f in fu.unnamed.iter() { + if let syn::Type::Tuple(tu) = &f.ty { + tuple = true; + for tty in tu.elems.iter() { + vtypes.append_all(quote! { + crate::knowledge::Hole < #tty > , + }); + } + } else { + let vty = &f.ty; + vtypes.append_all(quote! { + crate::knowledge::Hole < #vty > , + }); + } + } + } + if tuple { + variants.append_all(quote! { + #vnm (( #vtypes )) , + }); + } else { + variants.append_all(quote! { + #vnm ( #vtypes ) , + }); + } + cases.append_all(quote! { + Self :: #vnm (a) => Some( #name :: #vnm ( a.concretize()? )) , + }); + dcases.append_all(quote! { + Self :: #vnm {..} => #dnm :: #vnm , + }); + dispcases.append_all( + quote! { + Self :: #vnm (x) => { write!(f, "{}", #kebab)?; x.display_tuple(f) }, + }, + ); + } + let expanded = quote! { + #[derive(Debug, Clone, PartialEq, Eq, Hash, elfmacros::Unifiable, elfmacros::Substitutable)] + pub enum #hnm { + #variants + } + impl crate::knowledge::Concretizable for #hnm { + type Concrete = #name; + fn concretize(&self) -> Option<Self::Concrete> { + match self { + #cases + _ => None, + } + } + } + impl #hnm { + pub fn discriminant(&self) -> #dnm { + match self { + #dcases + } + } + } + impl std::fmt::Display for #hnm { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + #dispcases + } + } + } + impl std::fmt::Display for #name { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + #dispcases + } + } + } + }; + proc_macro::TokenStream::from(expanded) +} |
