Skip to content

Commit fdeaa90

Browse files
authored
Introduce new cgp-field constructs (#36)
* Add field, product, and sum types * Modify symbol! macro to use Cons and Nil to construct product * Implement parse_product macro function * Separate product type and product expr macro * Export product! and Product! macros * Add Sum! macro * Update derive(HasField) macro to use Value instead * Add changelog
1 parent 473260b commit fdeaa90

File tree

18 files changed

+250
-56
lines changed

18 files changed

+250
-56
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@
22

33
## Pre-Release
44

5+
- Introduce new cgp-field constructs [#36](https://github.com/contextgeneric/cgp/pull/36)
6+
- Introduce the product type constructs `Cons` and `Nil`.
7+
- Introduce the sum type constructs `Either` and `Void`.
8+
- Introduce the `Field` type for tagged field value.
9+
- Introduce the `Product!` macro for building product types.
10+
- Introduce the `product!` macro for building product expressions.
11+
- Introduce the `Sum!` macro for building sum types.
12+
- Change the `symbol!` macro to generate product type of `Char` using `Cons` and `Nil`.
13+
514
- Rename `HasField::Field` to `HasField::Value` - [#35](https://github.com/contextgeneric/cgp/pull/35)
615

716
- Remove `Sized` constraint from `Async` trait - [#34](https://github.com/contextgeneric/cgp/pull/34)

crates/cgp-core/src/prelude.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,6 @@ pub use cgp_component::{
33
define_components, delegate_components, derive_component, DelegateComponent, HasComponents,
44
};
55
pub use cgp_error::{CanRaiseError, HasErrorType};
6-
pub use cgp_field::{symbol, Char, HasField, HasFieldMut};
6+
pub use cgp_field::{
7+
product, symbol, Char, Cons, Either, HasField, HasFieldMut, Nil, Product, Sum, Void,
8+
};

crates/cgp-field-macro-lib/src/field.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@ pub fn derive_has_field_impls(item_struct: &ItemStruct) -> Vec<ItemImpl> {
2424
for #struct_ident #ty_generics
2525
#where_clause
2626
{
27-
type Field = #field_type;
27+
type Value = #field_type;
2828

2929
fn get_field(
3030
&self,
3131
key: ::core::marker::PhantomData< #field_symbol >,
32-
) -> &Self::Field
32+
) -> &Self::Value
3333
{
3434
&self. #field_ident
3535
}
@@ -44,7 +44,7 @@ pub fn derive_has_field_impls(item_struct: &ItemStruct) -> Vec<ItemImpl> {
4444
fn get_field_mut(
4545
&mut self,
4646
key: ::core::marker::PhantomData< #field_symbol >,
47-
) -> &mut Self::Field
47+
) -> &mut Self::Value
4848
{
4949
&mut self. #field_ident
5050
}

crates/cgp-field-macro-lib/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66
*/
77

88
pub mod field;
9+
pub mod product;
910
pub mod symbol;
1011

1112
#[cfg(test)]
1213
mod tests;
1314

1415
pub use field::derive_fields;
16+
pub use product::{make_product_expr, make_product_type, make_sum_type};
1517
pub use symbol::make_symbol;
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
use proc_macro2::TokenStream;
2+
use quote::quote;
3+
use syn::parse::{Parse, ParseStream};
4+
use syn::punctuated::Punctuated;
5+
use syn::token::Comma;
6+
use syn::{Expr, Type};
7+
8+
pub struct ParsePunctuated<T>(pub Punctuated<T, Comma>);
9+
10+
impl<T: Parse> Parse for ParsePunctuated<T> {
11+
fn parse(input: ParseStream) -> syn::Result<Self> {
12+
let types = Punctuated::parse_terminated(input)?;
13+
Ok(ParsePunctuated(types))
14+
}
15+
}
16+
17+
pub fn make_product_type(input: TokenStream) -> TokenStream {
18+
let types: ParsePunctuated<Type> = syn::parse2(input).unwrap();
19+
20+
types.0.iter().rfold(quote! { Nil }, |res, item| {
21+
quote! {
22+
Cons< #item , #res >
23+
}
24+
})
25+
}
26+
27+
pub fn make_sum_type(input: TokenStream) -> TokenStream {
28+
let types: ParsePunctuated<Type> = syn::parse2(input).unwrap();
29+
30+
types.0.iter().rfold(quote! { Void }, |res, item| {
31+
quote! {
32+
Either< #item , #res >
33+
}
34+
})
35+
}
36+
37+
pub fn make_product_expr(input: TokenStream) -> TokenStream {
38+
let types: ParsePunctuated<Expr> = syn::parse2(input).unwrap();
39+
40+
types.0.iter().rfold(quote! { Nil }, |res, item| {
41+
quote! {
42+
Cons( #item , #res )
43+
}
44+
})
45+
}

crates/cgp-field-macro-lib/src/symbol.rs

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
11
use proc_macro2::TokenStream;
22
use quote::ToTokens;
3-
use syn::punctuated::Punctuated;
4-
use syn::token::Comma;
53
use syn::{parse_quote, LitStr, Type};
64

75
pub fn symbol_from_string(value: &str) -> Type {
8-
let char_types = <Punctuated<Type, Comma>>::from_iter(
9-
value
10-
.chars()
11-
.map(|c: char| -> Type { parse_quote!( Char< #c > ) }),
12-
);
13-
14-
parse_quote!( ( #char_types ) )
6+
value
7+
.chars()
8+
.rfold(parse_quote! { Nil }, |tail, c: char| -> Type {
9+
parse_quote!( Cons< Char< #c >, #tail > )
10+
})
1511
}
1612

1713
pub fn make_symbol(input: TokenStream) -> TokenStream {

crates/cgp-field-macro-lib/src/tests/field.rs

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -13,42 +13,42 @@ fn test_basic_derive_fields() {
1313
});
1414

1515
let expected = quote! {
16-
impl HasField<(Char<'b'>, Char<'a'>, Char<'r'>)> for Foo {
17-
type Field = Bar;
16+
impl HasField<Cons<Char<'b'>, Cons<Char<'a'>, Cons<Char<'r'> , Nil>>>> for Foo {
17+
type Value = Bar;
1818

1919
fn get_field(
2020
&self,
21-
key: ::core::marker::PhantomData<(Char<'b'>, Char<'a'>, Char<'r'>)>,
22-
) -> &Self::Field {
21+
key: ::core::marker::PhantomData<Cons<Char<'b'>, Cons<Char<'a'>, Cons<Char<'r'> , Nil>>>>,
22+
) -> &Self::Value {
2323
&self.bar
2424
}
2525
}
2626

27-
impl HasFieldMut<(Char<'b'>, Char<'a'>, Char<'r'>)> for Foo {
27+
impl HasFieldMut<Cons<Char<'b'>, Cons<Char<'a'>, Cons<Char<'r'> , Nil>>>> for Foo {
2828
fn get_field_mut(
2929
&mut self,
30-
key: ::core::marker::PhantomData<(Char<'b'>, Char<'a'>, Char<'r'>)>,
31-
) -> &mut Self::Field {
30+
key: ::core::marker::PhantomData<Cons<Char<'b'>, Cons<Char<'a'>, Cons<Char<'r'> , Nil>>>>,
31+
) -> &mut Self::Value {
3232
&mut self.bar
3333
}
3434
}
3535

36-
impl HasField<(Char<'b'>, Char<'a'>, Char<'z'>)> for Foo {
37-
type Field = Baz;
36+
impl HasField<Cons<Char<'b'>, Cons<Char<'a'>, Cons<Char<'z'> , Nil>>>> for Foo {
37+
type Value = Baz;
3838

3939
fn get_field(
4040
&self,
41-
key: ::core::marker::PhantomData<(Char<'b'>, Char<'a'>, Char<'z'>)>,
42-
) -> &Self::Field {
41+
key: ::core::marker::PhantomData<Cons<Char<'b'>, Cons<Char<'a'>, Cons<Char<'z'> , Nil>>>>,
42+
) -> &Self::Value {
4343
&self.baz
4444
}
4545
}
4646

47-
impl HasFieldMut<(Char<'b'>, Char<'a'>, Char<'z'>)> for Foo {
47+
impl HasFieldMut<Cons<Char<'b'>, Cons<Char<'a'>, Cons<Char<'z'> , Nil>>>> for Foo {
4848
fn get_field_mut(
4949
&mut self,
50-
key: ::core::marker::PhantomData<(Char<'b'>, Char<'a'>, Char<'z'>)>,
51-
) -> &mut Self::Field {
50+
key: ::core::marker::PhantomData<Cons<Char<'b'>, Cons<Char<'a'>, Cons<Char<'z'> , Nil>>>>,
51+
) -> &mut Self::Value {
5252
&mut self.baz
5353
}
5454
}
@@ -70,58 +70,58 @@ fn test_generic_derive_fields() {
7070
});
7171

7272
let expected = quote! {
73-
impl<FooParamA, FooParamB: Clone> HasField<(Char<'b'>, Char<'a'>, Char<'r'>)>
73+
impl<FooParamA, FooParamB: Clone> HasField<Cons<Char<'b'>, Cons<Char<'a'>, Cons<Char<'r'> , Nil>>>>
7474
for Foo<FooParamA, FooParamB>
7575
where
7676
FooParamA: Eq,
7777
{
78-
type Field = Bar<FooParamA>;
78+
type Value = Bar<FooParamA>;
7979

8080
fn get_field(
8181
&self,
82-
key: ::core::marker::PhantomData<(Char<'b'>, Char<'a'>, Char<'r'>)>,
83-
) -> &Self::Field {
82+
key: ::core::marker::PhantomData<Cons<Char<'b'>, Cons<Char<'a'>, Cons<Char<'r'> , Nil>>>>,
83+
) -> &Self::Value {
8484
&self.bar
8585
}
8686
}
8787

88-
impl<FooParamA, FooParamB: Clone> HasFieldMut<(Char<'b'>, Char<'a'>, Char<'r'>)>
88+
impl<FooParamA, FooParamB: Clone> HasFieldMut<Cons<Char<'b'>, Cons<Char<'a'>, Cons<Char<'r'> , Nil>>>>
8989
for Foo<FooParamA, FooParamB>
9090
where
9191
FooParamA: Eq,
9292
{
9393
fn get_field_mut(
9494
&mut self,
95-
key: ::core::marker::PhantomData<(Char<'b'>, Char<'a'>, Char<'r'>)>,
96-
) -> &mut Self::Field {
95+
key: ::core::marker::PhantomData<Cons<Char<'b'>, Cons<Char<'a'>, Cons<Char<'r'> , Nil>>>>,
96+
) -> &mut Self::Value {
9797
&mut self.bar
9898
}
9999
}
100100

101-
impl<FooParamA, FooParamB: Clone> HasField<(Char<'b'>, Char<'a'>, Char<'z'>)>
101+
impl<FooParamA, FooParamB: Clone> HasField<Cons<Char<'b'>, Cons<Char<'a'>, Cons<Char<'z'> , Nil>>>>
102102
for Foo<FooParamA, FooParamB>
103103
where
104104
FooParamA: Eq,
105105
{
106-
type Field = Baz<String>;
106+
type Value = Baz<String>;
107107

108108
fn get_field(
109109
&self,
110-
key: ::core::marker::PhantomData<(Char<'b'>, Char<'a'>, Char<'z'>)>,
111-
) -> &Self::Field {
110+
key: ::core::marker::PhantomData<Cons<Char<'b'>, Cons<Char<'a'>, Cons<Char<'z'> , Nil>>>>,
111+
) -> &Self::Value {
112112
&self.baz
113113
}
114114
}
115115

116-
impl<FooParamA, FooParamB: Clone> HasFieldMut<(Char<'b'>, Char<'a'>, Char<'z'>)>
116+
impl<FooParamA, FooParamB: Clone> HasFieldMut<Cons<Char<'b'>, Cons<Char<'a'>, Cons<Char<'z'> , Nil>>>>
117117
for Foo<FooParamA, FooParamB>
118118
where
119119
FooParamA: Eq,
120120
{
121121
fn get_field_mut(
122122
&mut self,
123-
key: ::core::marker::PhantomData<(Char<'b'>, Char<'a'>, Char<'z'>)>,
124-
) -> &mut Self::Field {
123+
key: ::core::marker::PhantomData<Cons<Char<'b'>, Cons<Char<'a'>, Cons<Char<'z'> , Nil>>>>,
124+
) -> &mut Self::Value {
125125
&mut self.baz
126126
}
127127
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
pub mod field;
22
pub mod helper;
3+
pub mod product;
34
pub mod symbol;
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
use quote::quote;
2+
3+
use crate::product::{make_product_expr, make_product_type, make_sum_type};
4+
5+
#[test]
6+
fn test_product_type() {
7+
let derived = make_product_type(quote! {
8+
Foo,
9+
Bar<T>,
10+
Baz<T, U>,
11+
});
12+
13+
let expected = quote! {
14+
Cons<
15+
Foo,
16+
Cons<
17+
Bar<T>,
18+
Cons<
19+
Baz<T, U>,
20+
Nil> > >
21+
};
22+
23+
assert_eq!(derived.to_string(), expected.to_string());
24+
}
25+
26+
#[test]
27+
fn test_product_ident() {
28+
let derived = make_product_expr(quote! {
29+
foo,
30+
bar,
31+
baz,
32+
});
33+
34+
let expected = quote! {
35+
Cons(
36+
foo,
37+
Cons(
38+
bar,
39+
Cons(
40+
baz,
41+
Nil ) ) )
42+
};
43+
44+
assert_eq!(derived.to_string(), expected.to_string());
45+
}
46+
47+
#[test]
48+
fn test_product_expr() {
49+
let derived = make_product_expr(quote! {
50+
foo.0,
51+
Bar { bar },
52+
Baz::baz(),
53+
});
54+
55+
let expected = quote! {
56+
Cons(
57+
foo.0,
58+
Cons(
59+
Bar { bar },
60+
Cons(
61+
Baz::baz(),
62+
Nil ) ) )
63+
};
64+
65+
assert_eq!(derived.to_string(), expected.to_string());
66+
}
67+
68+
#[test]
69+
fn test_sum_type() {
70+
let derived = make_sum_type(quote! {
71+
Foo,
72+
Bar<T>,
73+
Baz<T, U>,
74+
});
75+
76+
let expected = quote! {
77+
Either<
78+
Foo,
79+
Either<
80+
Bar<T>,
81+
Either<
82+
Baz<T, U>,
83+
Void> > >
84+
};
85+
86+
assert_eq!(derived.to_string(), expected.to_string());
87+
}

crates/cgp-field-macro-lib/src/tests/symbol.rs

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,25 @@ use crate::tests::helper::equal::equal_token_stream;
55

66
#[test]
77
fn test_symbol_macro() {
8-
let symbol = make_symbol(quote!("hello_world"));
8+
let symbol = make_symbol(quote!("hello"));
99

1010
let derived = quote! {
1111
type Symbol = #symbol;
1212
};
1313

1414
let expected = quote! {
15-
type Symbol = (
15+
type Symbol = Cons<
1616
Char<'h'>,
17-
Char<'e'>,
18-
Char<'l'>,
19-
Char<'l'>,
20-
Char<'o'>,
21-
Char<'_'>,
22-
Char<'w'>,
23-
Char<'o'>,
24-
Char<'r'>,
25-
Char<'l'>,
26-
Char<'d'>,
27-
);
17+
Cons<
18+
Char<'e'>,
19+
Cons<
20+
Char<'l'>,
21+
Cons<
22+
Char<'l'>,
23+
Cons<
24+
Char<'o'>,
25+
Nil
26+
>>>>>;
2827
};
2928

3029
assert!(equal_token_stream(&derived, &expected));

0 commit comments

Comments
 (0)