diff options
| author | LLLL Colonq <llll@colonq> | 2026-10-12 01:31:00 -0400 |
|---|---|---|
| committer | LLLL Colonq <llll@colonq> | 2026-10-12 01:31:00 -0400 |
| commit | c4ee47647a5bf1a06ae88ebb9410117c927868ee (patch) | |
| tree | 0700a10322e649db6df9f110058a09af1e3ef26f /crates/elfmacros | |
| parent | 742fcf6793e1a38d4cf65bf72832847c0fd679b4 (diff) | |
Major refactor, generate relation traits with macros
Diffstat (limited to 'crates/elfmacros')
| -rw-r--r-- | crates/elfmacros/Cargo.toml | 4 | ||||
| -rw-r--r-- | crates/elfmacros/src/lib.rs | 182 |
2 files changed, 185 insertions, 1 deletions
diff --git a/crates/elfmacros/Cargo.toml b/crates/elfmacros/Cargo.toml index 9c97e8d..e286820 100644 --- a/crates/elfmacros/Cargo.toml +++ b/crates/elfmacros/Cargo.toml @@ -8,5 +8,7 @@ authors.workspace = true proc-macro = true [dependencies] +proc-macro2 = { workspace = true } syn = { workspace = true } -quote = { workspace = true }
\ No newline at end of file +quote = { workspace = true } +itertools = { workspace = true }
\ No newline at end of file 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) +} |
