summaryrefslogtreecommitdiff
path: root/crates/elfmacros/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/elfmacros/src/lib.rs')
-rw-r--r--crates/elfmacros/src/lib.rs182
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)
+}