Skip to content

Commit

Permalink
Introduce new cgp-field constructs (#36)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
soareschen authored Dec 7, 2024
1 parent 473260b commit fdeaa90
Show file tree
Hide file tree
Showing 18 changed files with 250 additions and 56 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@

## Pre-Release

- Introduce new cgp-field constructs [#36](https://github.com/contextgeneric/cgp/pull/36)
- Introduce the product type constructs `Cons` and `Nil`.
- Introduce the sum type constructs `Either` and `Void`.
- Introduce the `Field` type for tagged field value.
- Introduce the `Product!` macro for building product types.
- Introduce the `product!` macro for building product expressions.
- Introduce the `Sum!` macro for building sum types.
- Change the `symbol!` macro to generate product type of `Char` using `Cons` and `Nil`.

- Rename `HasField::Field` to `HasField::Value` - [#35](https://github.com/contextgeneric/cgp/pull/35)

- Remove `Sized` constraint from `Async` trait - [#34](https://github.com/contextgeneric/cgp/pull/34)
Expand Down
4 changes: 3 additions & 1 deletion crates/cgp-core/src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@ pub use cgp_component::{
define_components, delegate_components, derive_component, DelegateComponent, HasComponents,
};
pub use cgp_error::{CanRaiseError, HasErrorType};
pub use cgp_field::{symbol, Char, HasField, HasFieldMut};
pub use cgp_field::{
product, symbol, Char, Cons, Either, HasField, HasFieldMut, Nil, Product, Sum, Void,
};
6 changes: 3 additions & 3 deletions crates/cgp-field-macro-lib/src/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ pub fn derive_has_field_impls(item_struct: &ItemStruct) -> Vec<ItemImpl> {
for #struct_ident #ty_generics
#where_clause
{
type Field = #field_type;
type Value = #field_type;

fn get_field(
&self,
key: ::core::marker::PhantomData< #field_symbol >,
) -> &Self::Field
) -> &Self::Value
{
&self. #field_ident
}
Expand All @@ -44,7 +44,7 @@ pub fn derive_has_field_impls(item_struct: &ItemStruct) -> Vec<ItemImpl> {
fn get_field_mut(
&mut self,
key: ::core::marker::PhantomData< #field_symbol >,
) -> &mut Self::Field
) -> &mut Self::Value
{
&mut self. #field_ident
}
Expand Down
2 changes: 2 additions & 0 deletions crates/cgp-field-macro-lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
*/

pub mod field;
pub mod product;
pub mod symbol;

#[cfg(test)]
mod tests;

pub use field::derive_fields;
pub use product::{make_product_expr, make_product_type, make_sum_type};
pub use symbol::make_symbol;
45 changes: 45 additions & 0 deletions crates/cgp-field-macro-lib/src/product.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use proc_macro2::TokenStream;
use quote::quote;
use syn::parse::{Parse, ParseStream};
use syn::punctuated::Punctuated;
use syn::token::Comma;
use syn::{Expr, Type};

pub struct ParsePunctuated<T>(pub Punctuated<T, Comma>);

impl<T: Parse> Parse for ParsePunctuated<T> {
fn parse(input: ParseStream) -> syn::Result<Self> {
let types = Punctuated::parse_terminated(input)?;
Ok(ParsePunctuated(types))
}
}

pub fn make_product_type(input: TokenStream) -> TokenStream {
let types: ParsePunctuated<Type> = syn::parse2(input).unwrap();

types.0.iter().rfold(quote! { Nil }, |res, item| {
quote! {
Cons< #item , #res >
}
})
}

pub fn make_sum_type(input: TokenStream) -> TokenStream {
let types: ParsePunctuated<Type> = syn::parse2(input).unwrap();

types.0.iter().rfold(quote! { Void }, |res, item| {
quote! {
Either< #item , #res >
}
})
}

pub fn make_product_expr(input: TokenStream) -> TokenStream {
let types: ParsePunctuated<Expr> = syn::parse2(input).unwrap();

types.0.iter().rfold(quote! { Nil }, |res, item| {
quote! {
Cons( #item , #res )
}
})
}
14 changes: 5 additions & 9 deletions crates/cgp-field-macro-lib/src/symbol.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
use proc_macro2::TokenStream;
use quote::ToTokens;
use syn::punctuated::Punctuated;
use syn::token::Comma;
use syn::{parse_quote, LitStr, Type};

pub fn symbol_from_string(value: &str) -> Type {
let char_types = <Punctuated<Type, Comma>>::from_iter(
value
.chars()
.map(|c: char| -> Type { parse_quote!( Char< #c > ) }),
);

parse_quote!( ( #char_types ) )
value
.chars()
.rfold(parse_quote! { Nil }, |tail, c: char| -> Type {
parse_quote!( Cons< Char< #c >, #tail > )
})
}

pub fn make_symbol(input: TokenStream) -> TokenStream {
Expand Down
56 changes: 28 additions & 28 deletions crates/cgp-field-macro-lib/src/tests/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,42 +13,42 @@ fn test_basic_derive_fields() {
});

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

fn get_field(
&self,
key: ::core::marker::PhantomData<(Char<'b'>, Char<'a'>, Char<'r'>)>,
) -> &Self::Field {
key: ::core::marker::PhantomData<Cons<Char<'b'>, Cons<Char<'a'>, Cons<Char<'r'> , Nil>>>>,
) -> &Self::Value {
&self.bar
}
}

impl HasFieldMut<(Char<'b'>, Char<'a'>, Char<'r'>)> for Foo {
impl HasFieldMut<Cons<Char<'b'>, Cons<Char<'a'>, Cons<Char<'r'> , Nil>>>> for Foo {
fn get_field_mut(
&mut self,
key: ::core::marker::PhantomData<(Char<'b'>, Char<'a'>, Char<'r'>)>,
) -> &mut Self::Field {
key: ::core::marker::PhantomData<Cons<Char<'b'>, Cons<Char<'a'>, Cons<Char<'r'> , Nil>>>>,
) -> &mut Self::Value {
&mut self.bar
}
}

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

fn get_field(
&self,
key: ::core::marker::PhantomData<(Char<'b'>, Char<'a'>, Char<'z'>)>,
) -> &Self::Field {
key: ::core::marker::PhantomData<Cons<Char<'b'>, Cons<Char<'a'>, Cons<Char<'z'> , Nil>>>>,
) -> &Self::Value {
&self.baz
}
}

impl HasFieldMut<(Char<'b'>, Char<'a'>, Char<'z'>)> for Foo {
impl HasFieldMut<Cons<Char<'b'>, Cons<Char<'a'>, Cons<Char<'z'> , Nil>>>> for Foo {
fn get_field_mut(
&mut self,
key: ::core::marker::PhantomData<(Char<'b'>, Char<'a'>, Char<'z'>)>,
) -> &mut Self::Field {
key: ::core::marker::PhantomData<Cons<Char<'b'>, Cons<Char<'a'>, Cons<Char<'z'> , Nil>>>>,
) -> &mut Self::Value {
&mut self.baz
}
}
Expand All @@ -70,58 +70,58 @@ fn test_generic_derive_fields() {
});

let expected = quote! {
impl<FooParamA, FooParamB: Clone> HasField<(Char<'b'>, Char<'a'>, Char<'r'>)>
impl<FooParamA, FooParamB: Clone> HasField<Cons<Char<'b'>, Cons<Char<'a'>, Cons<Char<'r'> , Nil>>>>
for Foo<FooParamA, FooParamB>
where
FooParamA: Eq,
{
type Field = Bar<FooParamA>;
type Value = Bar<FooParamA>;

fn get_field(
&self,
key: ::core::marker::PhantomData<(Char<'b'>, Char<'a'>, Char<'r'>)>,
) -> &Self::Field {
key: ::core::marker::PhantomData<Cons<Char<'b'>, Cons<Char<'a'>, Cons<Char<'r'> , Nil>>>>,
) -> &Self::Value {
&self.bar
}
}

impl<FooParamA, FooParamB: Clone> HasFieldMut<(Char<'b'>, Char<'a'>, Char<'r'>)>
impl<FooParamA, FooParamB: Clone> HasFieldMut<Cons<Char<'b'>, Cons<Char<'a'>, Cons<Char<'r'> , Nil>>>>
for Foo<FooParamA, FooParamB>
where
FooParamA: Eq,
{
fn get_field_mut(
&mut self,
key: ::core::marker::PhantomData<(Char<'b'>, Char<'a'>, Char<'r'>)>,
) -> &mut Self::Field {
key: ::core::marker::PhantomData<Cons<Char<'b'>, Cons<Char<'a'>, Cons<Char<'r'> , Nil>>>>,
) -> &mut Self::Value {
&mut self.bar
}
}

impl<FooParamA, FooParamB: Clone> HasField<(Char<'b'>, Char<'a'>, Char<'z'>)>
impl<FooParamA, FooParamB: Clone> HasField<Cons<Char<'b'>, Cons<Char<'a'>, Cons<Char<'z'> , Nil>>>>
for Foo<FooParamA, FooParamB>
where
FooParamA: Eq,
{
type Field = Baz<String>;
type Value = Baz<String>;

fn get_field(
&self,
key: ::core::marker::PhantomData<(Char<'b'>, Char<'a'>, Char<'z'>)>,
) -> &Self::Field {
key: ::core::marker::PhantomData<Cons<Char<'b'>, Cons<Char<'a'>, Cons<Char<'z'> , Nil>>>>,
) -> &Self::Value {
&self.baz
}
}

impl<FooParamA, FooParamB: Clone> HasFieldMut<(Char<'b'>, Char<'a'>, Char<'z'>)>
impl<FooParamA, FooParamB: Clone> HasFieldMut<Cons<Char<'b'>, Cons<Char<'a'>, Cons<Char<'z'> , Nil>>>>
for Foo<FooParamA, FooParamB>
where
FooParamA: Eq,
{
fn get_field_mut(
&mut self,
key: ::core::marker::PhantomData<(Char<'b'>, Char<'a'>, Char<'z'>)>,
) -> &mut Self::Field {
key: ::core::marker::PhantomData<Cons<Char<'b'>, Cons<Char<'a'>, Cons<Char<'z'> , Nil>>>>,
) -> &mut Self::Value {
&mut self.baz
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/cgp-field-macro-lib/src/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod field;
pub mod helper;
pub mod product;
pub mod symbol;
87 changes: 87 additions & 0 deletions crates/cgp-field-macro-lib/src/tests/product.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
use quote::quote;

use crate::product::{make_product_expr, make_product_type, make_sum_type};

#[test]
fn test_product_type() {
let derived = make_product_type(quote! {
Foo,
Bar<T>,
Baz<T, U>,
});

let expected = quote! {
Cons<
Foo,
Cons<
Bar<T>,
Cons<
Baz<T, U>,
Nil> > >
};

assert_eq!(derived.to_string(), expected.to_string());
}

#[test]
fn test_product_ident() {
let derived = make_product_expr(quote! {
foo,
bar,
baz,
});

let expected = quote! {
Cons(
foo,
Cons(
bar,
Cons(
baz,
Nil ) ) )
};

assert_eq!(derived.to_string(), expected.to_string());
}

#[test]
fn test_product_expr() {
let derived = make_product_expr(quote! {
foo.0,
Bar { bar },
Baz::baz(),
});

let expected = quote! {
Cons(
foo.0,
Cons(
Bar { bar },
Cons(
Baz::baz(),
Nil ) ) )
};

assert_eq!(derived.to_string(), expected.to_string());
}

#[test]
fn test_sum_type() {
let derived = make_sum_type(quote! {
Foo,
Bar<T>,
Baz<T, U>,
});

let expected = quote! {
Either<
Foo,
Either<
Bar<T>,
Either<
Baz<T, U>,
Void> > >
};

assert_eq!(derived.to_string(), expected.to_string());
}
25 changes: 12 additions & 13 deletions crates/cgp-field-macro-lib/src/tests/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,25 @@ use crate::tests::helper::equal::equal_token_stream;

#[test]
fn test_symbol_macro() {
let symbol = make_symbol(quote!("hello_world"));
let symbol = make_symbol(quote!("hello"));

let derived = quote! {
type Symbol = #symbol;
};

let expected = quote! {
type Symbol = (
type Symbol = Cons<
Char<'h'>,
Char<'e'>,
Char<'l'>,
Char<'l'>,
Char<'o'>,
Char<'_'>,
Char<'w'>,
Char<'o'>,
Char<'r'>,
Char<'l'>,
Char<'d'>,
);
Cons<
Char<'e'>,
Cons<
Char<'l'>,
Cons<
Char<'l'>,
Cons<
Char<'o'>,
Nil
>>>>>;
};

assert!(equal_token_stream(&derived, &expected));
Expand Down
Loading

0 comments on commit fdeaa90

Please sign in to comment.