Skip to content

Commit

Permalink
Better step macro
Browse files Browse the repository at this point in the history
  • Loading branch information
taikiy committed Aug 10, 2023
1 parent 9d0a1b1 commit 25be6e4
Show file tree
Hide file tree
Showing 49 changed files with 4,378 additions and 23,041 deletions.
5 changes: 1 addition & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ step-trace = ["descriptive-gate"]
# The following two features are mutually exclusive. Descriptive should be enabled by deafult as the vast majority
# of unit tests use it. Compact uses memory-efficient gates and is suitable for production.
descriptive-gate = []
compact-gate = []
compact-gate = ["ipa-macros/compact-gate"]

[dependencies]
ipa-macros = { version = "*", path = "./ipa-macros" }
Expand Down Expand Up @@ -71,14 +71,11 @@ serde = { version = "1.0", optional = true, features = ["derive"] }
serde_json = { version = "1.0", optional = true }
sha2 = "0.10.6"
shuttle-crate = { package = "shuttle", version = "0.6.1", optional = true }
strum = {version = "0.25", features = ["derive"] }
thiserror = "1.0"
time = { version = "0.3", optional = true }
tinyvec = "1.6"
tokio = { version = "1.28", features = ["rt", "rt-multi-thread", "macros"] }
tokio-rustls = { version = "0.24.0", optional = true }
tokio-stream = "0.1.14"
tokio-util = "0.7.8"
toml = { version = "0.7", optional = true }
tower = { version = "0.4.13", optional = true }
tower-http = { version = "0.4.0", optional = true, features = ["trace"] }
Expand Down
124 changes: 0 additions & 124 deletions ipa-macros/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 4 additions & 12 deletions ipa-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,12 @@ version = "0.1.0"
rust-version = "1.64.0"
edition = "2021"

[lib]
proc-macro = true

[features]
trybuild = []
compact-gate = []

[[test]]
name = "tests"
path = "tests/mod.rs"
required-features = ["trybuild"]
[lib]
proc-macro = true

[dependencies]
syn = { version = "2.0.15", features = ["full"] }
syn = { version = "2.0.15", features = ["full", "extra-traits"] }
quote = "1.0.27"

[dev-dependencies]
trybuild = { version = "1.0.80", features = ["diff"] }
85 changes: 5 additions & 80 deletions ipa-macros/src/derive_gate/mod.rs
Original file line number Diff line number Diff line change
@@ -1,62 +1,10 @@
use crate::parser::{group_by_modules, ipa_state_transition_map, module_string_to_ast};
use crate::parser::{group_by_modules, ipa_state_transition_map};
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};

// Procedural macro to derive the Step and StepNarrow traits and generate a memory-efficient gate.
//
// The goal is to generate a state transition graph and the corresponding `StepNarrow` implementations
// for the IPA protocol. This macro assumes that a complete IPA steps file exists in the repo at the
// location specified as `STEPS_FILE`. The steps file can be generated by running `collect_steps.py`.
//
// The steps file contains a list of narrowed steps, where each line represents a hierarchy of narrowed
// steps delimited by "/". For example, the following lines represent a hierarchy of narrowed steps:
//
// RootStep => 0
// RootStep/StepA::A1 => 1
// RootStep/StepA::A1/StepB::B1 => 2
// RootStep/StepA::A1/StepB::B2 => 3
// RootStep/StepC::C1 => 4
// RootStep/StepC::C1/StepD::D1 => 5
// RootStep/StepC::C1/StepD::D1/StepA::A2 => 6
// RootStep/StepC::C2 => 7
//
// From these lines, we want to generate StepNarrow implementations for each step.
//
// impl StepNarrow<StepA> for Compact {
// fn narrow(&self, step: &StepA) -> Self {
// Self(match (self.0, step.as_ref()) {
// (0, "A1") => 1,
// (5, "A2") => 6,
// _ => panic!("invalid state transition"),
// })
// }
// }
// impl StepNarrow<StepB> for Compact {
// fn narrow(&self, step: &StepB) -> Self {
// Self(match (self.0, step.as_ref()) {
// (1, "B1") => 2,
// (1, "B2") => 3,
// _ => panic!("invalid state transition"),
// })
// }
// }
// ...
//
//
// Currently, this derive notation assumes it annotates the `Compact` struct defined in
// `src/protocol/step/compact.rs`. The `Compact` struct is a wrapper around a `u16` value that
// represents the current state of the IPA protocol.
//
// In the future, we might change the macro to annotate each step in the IPA protocol. The macro
// will then generate both `Descriptive` and `Compact` implementations for the step. However, that
// kind of derive macro requires more annotations such as the fully qualified module path of the
// step. This is because there are many locally-defined `Step` enums in IPA, and we need to
// disambiguate them. However, proc macro doesn't receive the fully qualified module path of the
// annotated struct.

/// Generate a state transition graph and the corresponding `StepNarrow` implementations for the
/// IPA protocol.
/// Generate a state transition graph and the corresponding `AsRef<str>`
/// and `deserialize()` implementations for `Compact` gate.
pub fn expand(item: TokenStream) -> TokenStream {
// `item` is the `struct Compact(u16)` in AST
let ast = parse_macro_input!(item as DeriveInput);
Expand All @@ -66,39 +14,16 @@ pub fn expand(item: TokenStream) -> TokenStream {
_ => panic!("derive Gate expects a struct"),
}

// we omit the fully qualified module path here because we want to be able to test the macro
// using our own implementations of `Step` and `StepNarrow`.
let mut expanded = quote!(
impl Step for #gate {}
impl crate::protocol::step::Step for #gate {}
);

let steps = ipa_state_transition_map();
let grouped_steps = group_by_modules(&steps);
let mut reverse_map = Vec::new();
let mut deserialize_map = Vec::new();

for (module, steps) in grouped_steps {
// generate the `StepNarrow` implementation for each module
let module = module_string_to_ast(&module);
let states = steps.iter().map(|s| {
let new_state = &s.name;
let new_state_id = s.id;
let previous_state_id = s.get_parent().unwrap().id;
quote!(
(#previous_state_id, #new_state) => #new_state_id,
)
});
expanded.extend(quote!(
impl StepNarrow<#module> for #gate {
fn narrow(&self, step: &#module) -> Self {
Self(match (self.0, step.as_ref()) {
#(#states)*
_ => static_state_map(self.0, step.as_ref()),
})
}
}
));

for (_, steps) in grouped_steps {
// generate the reverse map for `impl AsRef<str> for Compact`
// this is used to convert a state ID to a string representation of the state.
reverse_map.extend(steps.iter().map(|s| {
Expand Down
Loading

0 comments on commit 25be6e4

Please sign in to comment.