Compare commits
No commits in common. "c8d834306b897de13ac15908ebfb476079e7e94d" and "2af635550543c2d394a26d64fcf2d9c4be74aa97" have entirely different histories.
c8d834306b
...
2af6355505
9 changed files with 46 additions and 366 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,2 +1 @@
|
||||||
**/target
|
/target
|
||||||
|
|
||||||
|
|
49
Cargo.lock
generated
49
Cargo.lock
generated
|
@ -1,54 +1,7 @@
|
||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 3
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "derive_generic"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "generic"
|
name = "generic"
|
||||||
version = "0.1.0"
|
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"
|
|
||||||
|
|
|
@ -4,4 +4,3 @@ version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
derive_generic = { path = "derive_generic" }
|
|
||||||
|
|
47
derive_generic/Cargo.lock
generated
47
derive_generic/Cargo.lock
generated
|
@ -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"
|
|
|
@ -3,10 +3,4 @@ name = "derive_generic"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[lib]
|
|
||||||
proc-macro = true
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
proc-macro2 = "1"
|
|
||||||
syn = "2.0"
|
|
||||||
quote = "1"
|
|
||||||
|
|
|
@ -1,204 +1,14 @@
|
||||||
use syn::{parse_macro_input, spanned::Spanned, DeriveInput, Ident};
|
pub fn add(left: u64, right: u64) -> u64 {
|
||||||
use proc_macro::TokenStream;
|
left + right
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn special_from_data(data: &Data) -> proc_macro2::TokenStream {
|
#[cfg(test)]
|
||||||
match data {
|
mod tests {
|
||||||
Data::Struct(ref r#struct) => {
|
use super::*;
|
||||||
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();
|
|
||||||
|
|
||||||
let let_bind = generate_let_binds(bind_names.clone());
|
#[test]
|
||||||
|
fn it_works() {
|
||||||
quote! {
|
let result = add(2, 2);
|
||||||
let #let_bind = rep;
|
assert_eq!(result, 4);
|
||||||
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<Ident>) -> 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>) -> 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!") },
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,28 @@
|
||||||
use crate::generic;
|
use crate::generic::{Field, Sum, Generic};
|
||||||
use derive_generic::Generic;
|
|
||||||
|
|
||||||
#[derive(Generic)]
|
|
||||||
struct IntPairTuple (i16, i32);
|
|
||||||
|
|
||||||
#[derive(Generic)]
|
struct IntPair {
|
||||||
struct IntPairStruct {
|
|
||||||
a: i32,
|
a: i32,
|
||||||
b: i16,
|
b: i32
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Generic)]
|
impl Generic for IntPair {
|
||||||
struct UnitStruct;
|
type Representation = Sum<Field<i32>, Field<i32>>;
|
||||||
|
fn generalize(self) -> Self::Representation {
|
||||||
#[derive(Generic)]
|
Sum {
|
||||||
struct EmptyStruct {
|
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())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,63 +1,26 @@
|
||||||
use std::ops::Add;
|
pub struct Sum<Con, Rhs> where Con: IsConType, Rhs: IsRepType {
|
||||||
|
pub left: Con,
|
||||||
pub enum Sum<Con, Rhs> where Con: IsConType, Rhs: IsRepType {
|
pub right: Rhs,
|
||||||
Left(Con),
|
|
||||||
Right(Rhs),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Product<T, Rhs> where Rhs: IsConType {
|
pub struct Product<T, Rhs> where Rhs: IsConType {
|
||||||
pub left: T,
|
pub left: Field<T>,
|
||||||
pub right: Rhs
|
pub right: Rhs
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Field, Rest, RestRhs, FieldRhs> Add<Product<FieldRhs, RestRhs>> for Product<Field, Rest>
|
|
||||||
where
|
|
||||||
Rest: Add<RestRhs> + IsConType,
|
|
||||||
Field: Add<FieldRhs>,
|
|
||||||
RestRhs: IsConType,
|
|
||||||
Rest::Output: IsConType
|
|
||||||
{
|
|
||||||
type Output = Product<Field::Output, Rest::Output>;
|
|
||||||
|
|
||||||
fn add(self, other: Product<FieldRhs, RestRhs>) -> Self::Output {
|
|
||||||
Product {
|
|
||||||
left: self.left + other.left,
|
|
||||||
right: self.right + other.right
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub struct Field<T> {
|
pub struct Field<T> {
|
||||||
pub value: T
|
pub value: T
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R, T: Add<R>> Add<Field<R>> for Field<T> {
|
|
||||||
type Output=Field<T::Output>;
|
|
||||||
|
|
||||||
fn add(self, rhs: Field<R>) -> Self::Output {
|
|
||||||
Field {
|
|
||||||
value: self.value + rhs.value,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// types used for generic representation
|
// types used for generic representation
|
||||||
pub trait IsRepType {}
|
pub trait IsRepType {}
|
||||||
|
impl<Lhs: IsConType, Rhs: IsRepType> IsRepType for Sum<Lhs, Rhs> {}
|
||||||
impl<Lhs, Rhs> IsRepType for Sum<Lhs, Rhs>
|
impl<T: IsRepType, Rhs: IsConType> IsRepType for Product<T, Rhs> {}
|
||||||
where Lhs: IsConType, Rhs: IsRepType {}
|
|
||||||
|
|
||||||
impl<T, Rhs> IsRepType for Product<T, Rhs>
|
|
||||||
where Rhs: IsConType {}
|
|
||||||
|
|
||||||
impl<T> IsRepType for Field<T> {}
|
impl<T> IsRepType for Field<T> {}
|
||||||
|
|
||||||
// types that represent constructors
|
// types that represent constructors
|
||||||
pub trait IsConType {}
|
pub trait IsConType {}
|
||||||
impl<T, Rhs> IsConType for Product<T, Rhs>
|
impl<T, Rhs: IsConType> IsConType for Product<T, Rhs> {}
|
||||||
where Rhs: IsConType {}
|
|
||||||
impl<T> IsConType for Field<T> {}
|
impl<T> IsConType for Field<T> {}
|
||||||
|
|
||||||
|
|
||||||
|
@ -66,4 +29,3 @@ pub trait Generic {
|
||||||
fn generalize(self) -> Self::Representation;
|
fn generalize(self) -> Self::Representation;
|
||||||
fn specialize(rep: Self::Representation) -> Self;
|
fn specialize(rep: Self::Representation) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
use crate::generic::{Field, Generic, Product};
|
use crate::generic::{Field, Generic, Sum};
|
||||||
|
|
||||||
impl<A, B> Generic for (A, B) {
|
impl<A, B> Generic for (A, B) {
|
||||||
type Representation = Product<A, Field<B>>;
|
type Representation = Sum<Field<A>, Field<B>>;
|
||||||
|
|
||||||
fn generalize(self) -> Self::Representation {
|
fn generalize(self) -> Self::Representation {
|
||||||
let (l, r) = self;
|
let (l, r) = self;
|
||||||
Product {
|
Sum {
|
||||||
left: l,
|
left: Field { value: l },
|
||||||
right: Field { value: r }
|
right: Field { value: r }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn specialize(rep: Self::Representation) -> Self {
|
fn specialize(rep: Self::Representation) -> Self {
|
||||||
let Product {
|
let Sum {
|
||||||
left: a,
|
left: Field { value: a },
|
||||||
right: Field { value: b },
|
right: Field { value: b },
|
||||||
} = rep;
|
} = rep;
|
||||||
(a, b)
|
(a, b)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue