-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement
delegate_components!
as proc macro, and introduce `define…
…_components!` macro (#17) * Compare the derived token stream in test * Rename helper module to derive_component module * Draft AST and parser for delegate_components * Draft initial delegate_components macro * Test basic delegate_components macro call * Make generic params in target explicit * Parse target_generics as Generics * Propagate generics in component name * Move merge_generics to separate module * Draft with_components macro generator * Remove redelegate module * Name macro from components name * Fix generated with_components macro * Add new define_components! separate from delegate_components * Clean up * Move main macro code to plain library cgp-component-macro-lib * Refactoring * WIP * Add delegates to trait * Add generics to delegates to trait * Generalize substitution macro * Use new proc macros in cgp-core * Fix errors * Improve tests * Fix clippy
- Loading branch information
1 parent
9c323af
commit 89293a9
Showing
45 changed files
with
1,033 additions
and
334 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,7 @@ members = [ | |
"crates/cgp-sync", | ||
"crates/cgp-component", | ||
"crates/cgp-component-macro", | ||
"crates/cgp-component-macro-lib", | ||
"crates/cgp-error", | ||
"crates/cgp-error-eyre", | ||
"crates/cgp-error-std", | ||
|
@@ -24,12 +25,13 @@ repository = "https://github.com/informalsystems/cgp" | |
authors = ["Informal Systems <[email protected]>", "Soares Chen <[email protected]>"] | ||
|
||
[patch.crates-io] | ||
cgp-core = { path = "./crates/cgp-core" } | ||
cgp-async = { path = "./crates/cgp-async" } | ||
cgp-async-macro = { path = "./crates/cgp-async-macro" } | ||
cgp-sync = { path = "./crates/cgp-sync" } | ||
cgp-component = { path = "./crates/cgp-component" } | ||
cgp-component-macro = { path = "./crates/cgp-component-macro" } | ||
cgp-error = { path = "./crates/cgp-error" } | ||
cgp-run = { path = "./crates/cgp-run" } | ||
cgp-inner = { path = "./crates/cgp-inner" } | ||
cgp-core = { path = "./crates/cgp-core" } | ||
cgp-async = { path = "./crates/cgp-async" } | ||
cgp-async-macro = { path = "./crates/cgp-async-macro" } | ||
cgp-sync = { path = "./crates/cgp-sync" } | ||
cgp-component = { path = "./crates/cgp-component" } | ||
cgp-component-macro = { path = "./crates/cgp-component-macro" } | ||
cgp-component-macro-lib = { path = "./crates/cgp-component-macro-lib" } | ||
cgp-error = { path = "./crates/cgp-error" } | ||
cgp-run = { path = "./crates/cgp-run" } | ||
cgp-inner = { path = "./crates/cgp-inner" } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
[package] | ||
name = "cgp-component-macro-lib" | ||
version = "0.1.0" | ||
edition = { workspace = true } | ||
license = { workspace = true } | ||
repository = { workspace = true } | ||
authors = { workspace = true } | ||
rust-version = { workspace = true } | ||
readme = "README.md" | ||
keywords = ["context-generic programming"] | ||
description = """ | ||
Context-generic programming core macros | ||
""" | ||
|
||
[package.metadata.docs.rs] | ||
all-features = true | ||
|
||
[dependencies] | ||
syn = { version = "2.0.37", features = [ "full" ] } | ||
quote = "1.0.33" | ||
proc-macro2 = "1.0.67" | ||
itertools = "0.11.0" | ||
prettyplease = "0.2.20" |
139 changes: 139 additions & 0 deletions
139
crates/cgp-component-macro-lib/src/delegate_components/ast.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
use core::iter; | ||
|
||
use proc_macro2::TokenStream; | ||
use quote::ToTokens; | ||
use syn::parse::{Parse, ParseStream}; | ||
use syn::punctuated::Punctuated; | ||
use syn::token::{Bracket, Colon, Comma, Lt}; | ||
use syn::{braced, bracketed, Generics, Ident, Token, Type}; | ||
|
||
pub struct DelegateComponentsAst { | ||
pub target_type: Type, | ||
pub target_generics: Generics, | ||
pub delegate_entries: DelegateEntriesAst, | ||
} | ||
|
||
pub struct DefineComponentsAst { | ||
pub components_ident: Ident, | ||
pub components_generics: Generics, | ||
pub delegate_entries: DelegateEntriesAst, | ||
} | ||
|
||
pub struct DelegateEntriesAst { | ||
pub entries: Punctuated<DelegateEntryAst, Comma>, | ||
} | ||
|
||
pub struct DelegateEntryAst { | ||
pub components: Punctuated<ComponentAst, Comma>, | ||
pub source: Type, | ||
} | ||
|
||
#[derive(Clone)] | ||
pub struct ComponentAst { | ||
pub component_type: Type, | ||
pub component_generics: Generics, | ||
} | ||
|
||
impl DelegateEntriesAst { | ||
pub fn all_components(&self) -> Punctuated<ComponentAst, Comma> { | ||
self.entries | ||
.iter() | ||
.flat_map(|entry| entry.components.clone().into_iter()) | ||
.collect() | ||
} | ||
} | ||
|
||
impl Parse for DelegateComponentsAst { | ||
fn parse(input: ParseStream) -> syn::Result<Self> { | ||
let target_generics = if input.peek(Lt) { | ||
input.parse()? | ||
} else { | ||
Default::default() | ||
}; | ||
|
||
let target_type: Type = input.parse()?; | ||
|
||
let delegate_entries: DelegateEntriesAst = input.parse()?; | ||
|
||
Ok(Self { | ||
target_type, | ||
target_generics, | ||
delegate_entries, | ||
}) | ||
} | ||
} | ||
|
||
impl Parse for DefineComponentsAst { | ||
fn parse(input: ParseStream) -> syn::Result<Self> { | ||
let components_ident: Ident = input.parse()?; | ||
|
||
let components_generics = if input.peek(Lt) { | ||
input.parse()? | ||
} else { | ||
Default::default() | ||
}; | ||
|
||
let delegate_entries: DelegateEntriesAst = input.parse()?; | ||
|
||
Ok(Self { | ||
components_ident, | ||
components_generics, | ||
delegate_entries, | ||
}) | ||
} | ||
} | ||
|
||
impl Parse for DelegateEntriesAst { | ||
fn parse(input: ParseStream) -> syn::Result<Self> { | ||
let entries = { | ||
let entries_body; | ||
braced!(entries_body in input); | ||
entries_body.parse_terminated(DelegateEntryAst::parse, Comma)? | ||
}; | ||
|
||
Ok(Self { entries }) | ||
} | ||
} | ||
|
||
impl Parse for DelegateEntryAst { | ||
fn parse(input: ParseStream) -> syn::Result<Self> { | ||
let components = if input.peek(Bracket) { | ||
let components_body; | ||
bracketed!(components_body in input); | ||
components_body.parse_terminated(ComponentAst::parse, Token![,])? | ||
} else { | ||
let component: ComponentAst = input.parse()?; | ||
Punctuated::from_iter(iter::once(component)) | ||
}; | ||
|
||
let _: Colon = input.parse()?; | ||
|
||
let source: Type = input.parse()?; | ||
|
||
Ok(Self { components, source }) | ||
} | ||
} | ||
|
||
impl Parse for ComponentAst { | ||
fn parse(input: ParseStream) -> syn::Result<Self> { | ||
let component_generics = if input.peek(Lt) { | ||
input.parse()? | ||
} else { | ||
Default::default() | ||
}; | ||
|
||
let component_type: Type = input.parse()?; | ||
|
||
Ok(Self { | ||
component_type, | ||
component_generics, | ||
}) | ||
} | ||
} | ||
|
||
impl ToTokens for ComponentAst { | ||
fn to_tokens(&self, tokens: &mut TokenStream) { | ||
tokens.extend(self.component_generics.to_token_stream()); | ||
tokens.extend(self.component_type.to_token_stream()); | ||
} | ||
} |
67 changes: 67 additions & 0 deletions
67
crates/cgp-component-macro-lib/src/delegate_components/define.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
use proc_macro2::{Span, TokenStream}; | ||
use quote::ToTokens; | ||
use syn::{parse_quote, Ident}; | ||
|
||
use crate::delegate_components::ast::DefineComponentsAst; | ||
use crate::delegate_components::define_struct::define_struct; | ||
use crate::delegate_components::delegates_to::define_delegates_to_trait; | ||
use crate::delegate_components::impl_delegate::impl_delegate_components; | ||
use crate::delegate_components::substitution_macro::define_substitution_macro; | ||
use crate::derive_component::snake_case::to_snake_case_str; | ||
|
||
pub fn define_components(body: TokenStream) -> TokenStream { | ||
let ast: DefineComponentsAst = syn::parse2(body).unwrap(); | ||
|
||
let components_type = { | ||
let components_ident = &ast.components_ident; | ||
let type_generics = ast.components_generics.split_for_impl().1; | ||
parse_quote!( #components_ident #type_generics ) | ||
}; | ||
|
||
let impl_items = impl_delegate_components( | ||
&components_type, | ||
&ast.components_generics, | ||
&ast.delegate_entries, | ||
); | ||
|
||
let item_struct = define_struct(&ast.components_ident, &ast.components_generics); | ||
|
||
let mut output = TokenStream::new(); | ||
|
||
output.extend(item_struct.to_token_stream()); | ||
|
||
for impl_item in impl_items { | ||
output.extend(impl_item.to_token_stream()); | ||
} | ||
|
||
{ | ||
let delegates_to_trait_name = format!("DelegatesTo{}", ast.components_ident); | ||
|
||
let (delegates_to_trait, delegates_to_impl) = define_delegates_to_trait( | ||
&Ident::new(&delegates_to_trait_name, Span::call_site()), | ||
&components_type, | ||
&ast.components_generics, | ||
&ast.delegate_entries, | ||
); | ||
|
||
output.extend(delegates_to_trait.to_token_stream()); | ||
output.extend(delegates_to_impl.to_token_stream()); | ||
} | ||
|
||
{ | ||
let with_components_macro_name = format!( | ||
"with_{}", | ||
to_snake_case_str(&ast.components_ident.to_string()) | ||
); | ||
|
||
let with_components_macro = define_substitution_macro( | ||
&Ident::new(&with_components_macro_name, Span::call_site()), | ||
&ast.components_ident, | ||
&ast.delegate_entries.all_components().to_token_stream(), | ||
); | ||
|
||
output.extend(with_components_macro); | ||
} | ||
|
||
output | ||
} |
40 changes: 40 additions & 0 deletions
40
crates/cgp-component-macro-lib/src/delegate_components/define_struct.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
use syn::punctuated::Punctuated; | ||
use syn::token::Comma; | ||
use syn::{parse_quote, GenericParam, Generics, Ident, ItemStruct, Type}; | ||
|
||
pub fn define_struct(ident: &Ident, generics: &Generics) -> ItemStruct { | ||
if generics.params.is_empty() { | ||
parse_quote! { | ||
pub struct #ident; | ||
} | ||
} else { | ||
let mut generic_params = generics.params.clone(); | ||
let mut phantom_params: Punctuated<Type, Comma> = Default::default(); | ||
|
||
for param in generic_params.iter_mut() { | ||
match param { | ||
GenericParam::Type(type_param) => { | ||
type_param.colon_token = None; | ||
type_param.bounds.clear(); | ||
|
||
let type_ident = &type_param.ident; | ||
phantom_params.push(parse_quote!( #type_ident )); | ||
} | ||
GenericParam::Lifetime(life_param) => { | ||
life_param.colon_token = None; | ||
life_param.bounds.clear(); | ||
|
||
let lifetime = &life_param.lifetime; | ||
phantom_params.push(parse_quote!( & #lifetime () )); | ||
} | ||
_ => {} | ||
} | ||
} | ||
|
||
parse_quote! { | ||
pub struct #ident < #generic_params > ( | ||
pub ::core::marker::PhantomData<( #phantom_params )> | ||
); | ||
} | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
crates/cgp-component-macro-lib/src/delegate_components/delegate.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
use proc_macro2::TokenStream; | ||
use quote::ToTokens; | ||
|
||
use crate::delegate_components::ast::DelegateComponentsAst; | ||
use crate::delegate_components::impl_delegate::impl_delegate_components; | ||
|
||
pub fn delegate_components(body: TokenStream) -> TokenStream { | ||
let ast: DelegateComponentsAst = syn::parse2(body).unwrap(); | ||
|
||
let impl_items = impl_delegate_components( | ||
&ast.target_type, | ||
&ast.target_generics, | ||
&ast.delegate_entries, | ||
); | ||
|
||
let mut output = TokenStream::new(); | ||
|
||
for impl_item in impl_items { | ||
output.extend(impl_item.to_token_stream()); | ||
} | ||
|
||
output | ||
} |
Oops, something went wrong.