diff --git a/.gitignore b/.gitignore index 576f200..ea8c4bf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ -**/target - +/target diff --git a/Cargo.lock b/Cargo.lock index 146484d..b69769b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,54 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 4 - -[[package]] -name = "derive_generic" -version = "0.1.0" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] +version = 3 [[package]] name = "generic" version = "0.1.0" -dependencies = [ - "derive_generic", -] - -[[package]] -name = "proc-macro2" -version = "1.0.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "syn" -version = "2.0.101" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "unicode-ident" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" diff --git a/Cargo.toml b/Cargo.toml index e4b6eee..c1c4ae5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,4 +4,3 @@ version = "0.1.0" edition = "2021" [dependencies] -derive_generic = { path = "derive_generic" } diff --git a/derive_generic/Cargo.lock b/derive_generic/Cargo.lock deleted file mode 100644 index 19a08a2..0000000 --- a/derive_generic/Cargo.lock +++ /dev/null @@ -1,47 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "derive_generic" -version = "0.1.0" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "proc-macro2" -version = "1.0.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "syn" -version = "2.0.101" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "unicode-ident" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" diff --git a/derive_generic/Cargo.toml b/derive_generic/Cargo.toml index 3c3d21f..695be54 100644 --- a/derive_generic/Cargo.toml +++ b/derive_generic/Cargo.toml @@ -3,10 +3,4 @@ name = "derive_generic" version = "0.1.0" edition = "2021" -[lib] -proc-macro = true - [dependencies] -proc-macro2 = "1" -syn = "2.0" -quote = "1" diff --git a/derive_generic/src/lib.rs b/derive_generic/src/lib.rs index aaecf7f..b93cf3f 100644 --- a/derive_generic/src/lib.rs +++ b/derive_generic/src/lib.rs @@ -1,204 +1,14 @@ -use syn::{parse_macro_input, spanned::Spanned, DeriveInput, Ident}; -use proc_macro::TokenStream; -use quote::{quote, ToTokens}; -use syn::{Data, Fields, Index, Type}; - -#[proc_macro] -pub fn derive_generic_raw(input: TokenStream) -> TokenStream { - let ast = parse_macro_input!(input as DeriveInput); - - let data_name = ast.ident; - - let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); - - let repr = repr_from_data(&ast.data); - - let generalization = general_from_data(&ast.data); - - let specialization = special_from_data(&ast.data); - - quote! { - impl #impl_generics generic::Generic for #data_name #ty_generics #where_clause { - type Representation = #repr; - - fn generalize(self) -> Self::Representation { - #generalization - } - fn specialize(rep: Self::Representation) -> Self { - #specialization - } - } - }.into() +pub fn add(left: u64, right: u64) -> u64 { + left + right } -fn special_from_data(data: &Data) -> proc_macro2::TokenStream { - match data { - Data::Struct(ref r#struct) => { - match &r#struct.fields { - Fields::Named(ref fields) => { - let bind_names: Vec<_> = fields.named.iter() - .map(|field| field.ident.clone().expect("Named fields must have an identifier.")) - .collect(); +#[cfg(test)] +mod tests { + use super::*; - let let_bind = generate_let_binds(bind_names.clone()); - - quote! { - let #let_bind = rep; - Self { - #(#bind_names ,)* - } - } - }, - Fields::Unnamed(ref fields) => { - let bind_names: Vec<_> = fields.unnamed.iter() - .enumerate() - .map(|(i, field)| Ident::new(&format!("field_{i}"), field.span())) - .collect(); - - let let_bind = generate_let_binds(bind_names.clone()); - - quote! { - let #let_bind = rep; - Self( #(#bind_names ,)* ) - } - }, - Fields::Unit => quote! { - Self - }, - } - }, - Data::Enum(data_enum) => todo!(), - Data::Union(data_union) => todo!(), - } -} - -fn generate_let_binds(mut bind_names: Vec) -> proc_macro2::TokenStream { - match bind_names.len() { - 0 => quote! { - _ - }, - _ => { - let last = bind_names.pop().unwrap(); - let last = quote! { - generic::Field { value: #last } - }; - - bind_names.iter().fold(last, |acc, i| - quote! { - generic::Product { - left: #i, - right: #acc - } - } - ) - } - } -} - -fn general_from_data(data: &Data) -> proc_macro2::TokenStream { - match data { - Data::Struct(ref r#struct) => { - match &r#struct.fields { - Fields::Named(ref fields) => { - let names: Vec<_> = fields.named.iter() - .map(|field| field.ident.as_ref() - .expect("Named Fields must have an identifier") - .to_token_stream() - ) - .collect(); - - match names.len() { - 0 => quote! { - let _ = self; - generic::Field { value: () } - }, - _ => general_from_names(names) - } - - - }, - Fields::Unnamed(ref fields) => { - let indices: Vec<_> = fields.unnamed.iter() - .enumerate() - .map(|(i, _)| Index::from(i).to_token_stream()) - .collect(); - - match fields.unnamed.len() { - 0 => quote! { - let _ = self; - generic::Field { value: () } - }, - _ => general_from_names(indices) - } - }, - Fields::Unit => quote! { generic::Field { value: () } }, - } - }, - Data::Enum(data_enum) => todo!(), - Data::Union(data_union) => quote!{}, - } -} - -fn general_from_names(mut names: Vec) -> proc_macro2::TokenStream { - let last = names.pop().expect("Impossible"); - let expr = quote! { generic::Field { value: self.#last } }; - - names.iter().fold(expr, |e, i| - quote! { - generic::Product { - left: self.#i, - right: #e - } - } - ) -} - -#[proc_macro_derive(Generic)] -pub fn derive_generic(input: TokenStream) -> TokenStream { - derive_generic_raw(input) -} - -fn product_from_types(mut types: Vec<&Type>) -> proc_macro2::TokenStream { - let last = types.pop().unwrap(); - let last = quote! { generic::Field<#last> }; - - types.iter().fold(last, |e, i| - quote! { - generic::Product<#i, #e> - }.into_token_stream() - ) -} - -fn repr_from_data(data: &Data) -> proc_macro2::TokenStream { - match data { - Data::Struct(ref r#struct) => { - match &r#struct.fields { - Fields::Named(ref fields) => { - let v: Vec<_> = fields.named - .iter() - .map(|f| &f.ty) - .collect(); - - match v.len() { - 0 => quote! { generic::Field<()> }, - _ => product_from_types(v) - } - }, - Fields::Unnamed(ref fields) => { - let v: Vec<_> = fields.unnamed - .iter() - .map(|f| &f.ty) - .collect(); - - match v.len() { - 0 => quote! { generic::Field<()> }, - _ => product_from_types(v) - } - }, - Fields::Unit => quote! { generic::Field<()> }, - } - }, - Data::Enum(data_enum) => todo!(), - Data::Union(_) => quote! { compile_error!("Cannot derive Generics for union types!") }, + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); } } diff --git a/src/example.rs b/src/example.rs index 2cab1f0..8b92e11 100644 --- a/src/example.rs +++ b/src/example.rs @@ -1,18 +1,28 @@ -use crate::generic; -use derive_generic::Generic; +use crate::generic::{Field, Sum, Generic}; -#[derive(Generic)] -struct IntPairTuple (i16, i32); -#[derive(Generic)] -struct IntPairStruct { +struct IntPair { a: i32, - b: i16, + b: i32 } -#[derive(Generic)] -struct UnitStruct; - -#[derive(Generic)] -struct EmptyStruct { +impl Generic for IntPair { + type Representation = Sum, Field>; + fn generalize(self) -> Self::Representation { + Sum { + left: Field { value: self.a }, + right: Field { value: self.b } + } + } + fn specialize(rep: Self::Representation) -> Self { + let Sum { + left: Field { value: a }, + right: Field { value: b }, + } = rep; + IntPair { a, b } + } +} + +fn convert(x: IntPair) -> (i32, i32) { + Generic::specialize(x.generalize()) } diff --git a/src/generic.rs b/src/generic.rs index b5f2cbb..c12c299 100644 --- a/src/generic.rs +++ b/src/generic.rs @@ -1,63 +1,26 @@ -use std::ops::Add; - -pub enum Sum where Con: IsConType, Rhs: IsRepType { - Left(Con), - Right(Rhs), +pub struct Sum where Con: IsConType, Rhs: IsRepType { + pub left: Con, + pub right: Rhs, } pub struct Product where Rhs: IsConType { - pub left: T, + pub left: Field, pub right: Rhs } -impl Add> for Product -where - Rest: Add + IsConType, - Field: Add, - RestRhs: IsConType, - Rest::Output: IsConType -{ - type Output = Product; - - fn add(self, other: Product) -> Self::Output { - Product { - left: self.left + other.left, - right: self.right + other.right - } - } -} - - pub struct Field { pub value: T } -impl> Add> for Field { - type Output=Field; - - fn add(self, rhs: Field) -> Self::Output { - Field { - value: self.value + rhs.value, - } - } -} - - // types used for generic representation pub trait IsRepType {} - -impl IsRepType for Sum - where Lhs: IsConType, Rhs: IsRepType {} - -impl IsRepType for Product - where Rhs: IsConType {} - +impl IsRepType for Sum {} +impl IsRepType for Product {} impl IsRepType for Field {} // types that represent constructors pub trait IsConType {} -impl IsConType for Product - where Rhs: IsConType {} +impl IsConType for Product {} impl IsConType for Field {} @@ -66,4 +29,3 @@ pub trait Generic { fn generalize(self) -> Self::Representation; fn specialize(rep: Self::Representation) -> Self; } - diff --git a/src/instances.rs b/src/instances.rs index 3dd4519..f470fd1 100644 --- a/src/instances.rs +++ b/src/instances.rs @@ -1,19 +1,19 @@ -use crate::generic::{Field, Generic, Product}; +use crate::generic::{Field, Generic, Sum}; impl Generic for (A, B) { - type Representation = Product>; + type Representation = Sum, Field>; fn generalize(self) -> Self::Representation { let (l, r) = self; - Product { - left: l, + Sum { + left: Field { value: l }, right: Field { value: r } } } fn specialize(rep: Self::Representation) -> Self { - let Product { - left: a, + let Sum { + left: Field { value: a }, right: Field { value: b }, } = rep; (a, b)