aboutsummaryrefslogtreecommitdiff
path: root/guests_macro
diff options
context:
space:
mode:
authorKamen Mladenov <kamen@syndamia.com>2025-02-10 13:23:04 +0200
committerKamen Mladenov <kamen@syndamia.com>2025-02-10 13:23:04 +0200
commitb3cfd4b13c95a2beb6ee7a0aa6b0f72752b492aa (patch)
tree5c546f33a4388f13d8066fa6a185c9b31c8a25d3 /guests_macro
parentceed1b4a33a9b8d0580bd35493f34102ae59d184 (diff)
downloadzkVMs-benchmarks-b3cfd4b13c95a2beb6ee7a0aa6b0f72752b492aa.tar
zkVMs-benchmarks-b3cfd4b13c95a2beb6ee7a0aa6b0f72752b492aa.tar.gz
zkVMs-benchmarks-b3cfd4b13c95a2beb6ee7a0aa6b0f72752b492aa.zip
docs(guests_macro): Add detailed documentation comments
Diffstat (limited to 'guests_macro')
-rw-r--r--guests_macro/Cargo.toml1
-rw-r--r--guests_macro/src/lib.rs36
-rw-r--r--guests_macro/src/parse_fn.rs66
3 files changed, 87 insertions, 16 deletions
diff --git a/guests_macro/Cargo.toml b/guests_macro/Cargo.toml
index 4a2e538..6173b91 100644
--- a/guests_macro/Cargo.toml
+++ b/guests_macro/Cargo.toml
@@ -1,5 +1,6 @@
[package]
name = "guests_macro"
+description = "Helper macros for guests and wrapper_macros"
version = "0.1.0"
edition = "2021"
diff --git a/guests_macro/src/lib.rs b/guests_macro/src/lib.rs
index 469a2da..30bfec7 100644
--- a/guests_macro/src/lib.rs
+++ b/guests_macro/src/lib.rs
@@ -2,11 +2,45 @@ use std::{ fs::File, io::Write };
use proc_macro::TokenStream;
mod parse_fn;
+/// Create an `entrypoint_expr` macro inside the guest program. This will be
+/// used inside the ZKVM guest program to call the ZKVM guest wrapper's
+/// `make_wrapper` macro with entrypoint function type.
+///
+/// The overarching goal is to call `make_wrapper` with an input, which
+/// contains the function definition. This can only happen inside the guest,
+/// since that is the only place where we have the definition (syntactically).
+/// But you can only pass data to macros by "inlining" it as macro arguments
+/// (i.e. macros work on syntax, so creating a variable wouldn't work). Also,
+/// the `make_wrapper` definition doesn't exist in the guest, but the ZKVM
+/// wrapper crate. For these reasons we create a macro which invokes
+/// `make_wrapper` with the proper arguments.
+///
+/// # Usage
+///
+/// Inside your guest (under guests directory) add an attribute above your main
+/// (entrypoint/start) function. It takes no arguments.
+///
+/// ```rust
+/// #[guests_macro::proving_entrypoint]
+/// fn main(...) -> ... { ..... }
+/// ```
+///
+/// # Example output
+///
+/// ```rust
+/// #[macro_export]
+/// macro_rules! entrypoint_expr {
+/// () => {
+/// make_wrapper!{fn main(...) -> ...}
+/// };
+/// }
+/// ```
#[proc_macro_attribute]
pub fn proving_entrypoint(_: TokenStream, mut item: TokenStream) -> TokenStream {
let (name, args, ret) = parse_fn::split_fn(&item);
- // Put the file in zkVMs-benchmarks/guests/
+ // We also need to pass some type information to the host program compile-time.
+ // Put it in the file guests/type.txt.
let mut output = File::create("../type.txt").unwrap();
writeln!(output, "{}", &format!("{args}").replace('\n', " "));
write!(output, "{}", &format!("{ret}").replace('\n', " "));
diff --git a/guests_macro/src/parse_fn.rs b/guests_macro/src/parse_fn.rs
index c5006a8..87b0073 100644
--- a/guests_macro/src/parse_fn.rs
+++ b/guests_macro/src/parse_fn.rs
@@ -1,7 +1,16 @@
+//! A "library" with procedural macro functions for parsing and handling
+//! function definitions.
+//!
+//! As of writing, exporting procedural macro functions is not supported.
+//! Therefore, it should be used in ZKVM wrapper_macro crates, via a [mod path
+//! attribute](https://doc.rust-lang.org/reference/items/modules.html#the-path-attribute).
+
use proc_macro::{ TokenStream, TokenTree, Delimiter, Spacing, Group };
-/// Input: "fn name(...) -> ... { ... }"
-/// Output: "name", "(...)", "..."
+/// Split function definition into triplet of name, arguments and output types.
+///
+/// **Input:** "fn name(...) -> ... { ..... }"
+/// **Output:** "name", "(...)", "..."
pub fn split_fn(item: &TokenStream) -> (TokenStream, TokenStream, TokenStream) {
let item = item.clone().into_iter();
@@ -44,8 +53,11 @@ pub fn split_fn(item: &TokenStream) -> (TokenStream, TokenStream, TokenStream) {
(name, args, ret)
}
-/// Input: "(p1 : t1, p2: t2, ...)"
-/// Output: vec!["p1 : t1", "p2: t2", ...]
+/// Split arguments group into a vector of each argument with it's associated
+/// type.
+///
+/// **Input:** "(p1 : t1, p2 : t2, ...)"
+/// **Output:** vec!["p1 : t1", "p2 : t2", ...]
pub fn args_split(item: &TokenStream) -> Vec<TokenStream> {
let contents;
if let TokenTree::Group(group) = item.clone().into_iter().next().unwrap() {
@@ -62,6 +74,9 @@ pub fn args_split(item: &TokenStream) -> Vec<TokenStream> {
for tt in contents {
match tt {
TokenTree::Punct(ref punct) => match punct.as_char() {
+ // < and > do **not** form TokenTree groups, however their
+ // usage is like that of a group. Hence, we need extra
+ // logic to skip them.
'<' => angle_level += 1,
'>' => angle_level -= 1,
',' => if angle_level == 0 {
@@ -83,8 +98,13 @@ pub fn args_split(item: &TokenStream) -> Vec<TokenStream> {
args
}
-/// Input: "(p1 : t1, p2: t2, ...)"
-/// Output: vec!["p1 : t1", "p2: t2", ...], vec!["p1 : t1", "p2: t2", ...]
+/// Like `args_split`, however two vectors are returned: the first for public
+/// arguments (and their types) and the second for private ones.
+///
+/// `public` is a vector of argument names.
+///
+/// **Input:** "(p1 : t1, p2: t2, ...)", vec!["p3", "p4", ...]
+/// **Output:** vec!["p1 : t1", "p2: t2", ...], vec!["p3 : t3", "p4: t4", ...]
pub fn args_split_public(item: &TokenStream, public: &Vec<&String>) -> (Vec<TokenStream>, Vec<TokenStream>) {
let all_args = args_split(item);
let public_args: Vec<TokenStream> = all_args
@@ -99,15 +119,18 @@ pub fn args_split_public(item: &TokenStream, public: &Vec<&String>) -> (Vec<Toke
(public_args, private_args)
}
-/// Input: "(p1 : t1, p2: t2, ...)"
-/// Output: vec![p1, p2, ...], vec![t1, t2, ...]
+/// Split arguments group into two vectors: one for all argument names and one
+/// for every argument type.
+///
+/// **Input:** "(p1 : t1, p2: t2, ...)"
+/// **Output:** vec!["p1", "p2", ...], vec!["t1", "t2", ...]
pub fn args_divide(item: &TokenStream) -> (Vec<TokenStream>, Vec<TokenStream>) {
let contents;
if let TokenTree::Group(group) = item.clone().into_iter().next().unwrap() {
contents = group.stream().into_iter();
}
else {
- unreachable!();
+ unreachable!("Item passed to args_divide is not a group: \"{item}\"");
}
let mut patterns = Vec::new();
@@ -119,11 +142,15 @@ pub fn args_divide(item: &TokenStream) -> (Vec<TokenStream>, Vec<TokenStream>) {
for tt in contents {
match tt {
TokenTree::Punct(ref punct) => {
+ // Ignore "::"
if punct.spacing() == Spacing::Joint && punct.as_char() == ':' {
ignore_next = true;
}
else if !ignore_next {
match punct.as_char() {
+ // < and > do **not** form TokenTree groups, however their
+ // usage is like that of a group. Hence, we need extra
+ // logic to skip them.
'<' => angle_level += 1,
'>' => angle_level -= 1,
':' => {
@@ -153,8 +180,13 @@ pub fn args_divide(item: &TokenStream) -> (Vec<TokenStream>, Vec<TokenStream>) {
(patterns, types)
}
-/// Input: "(p1 : t1, p2: t2, ...)"
-/// Output: (vec![p1, p2, ...], vec![t1, t2, ...]), (vec![p1, p2, ...], vec![t1, t2, ...])
+/// Like `args_divide`, however two tuples of vectors are returned: the first
+/// for public arguments and types, and the second for private ones.
+///
+/// `public` is a vector of argument names.
+///
+/// **Input:** "(p1 : t1, p2: t2, ...)", vec!["p3", "p4", ...]
+/// **Output:** (vec!["p1", "p2", ...], vec!["t1", "t2", ...]), (vec!["p3", "p4", ...], vec!["t3", "t4", ...])
pub fn args_divide_public(item: &TokenStream, public: &Vec<&String>) -> ((Vec<TokenStream>, Vec<TokenStream>), (Vec<TokenStream>, Vec<TokenStream>)) {
let (patterns, types) = args_divide(item);
@@ -173,15 +205,19 @@ pub fn args_divide_public(item: &TokenStream, public: &Vec<&String>) -> ((Vec<To
((public_patterns, public_types), (private_patterns, private_types))
}
-/// Input: "(p1 : t1, p2: t2, ...)"
-/// Output: "(p1, p2, ...)", "(t1, t2, ...)"
+/// Like `args_divide`, but group arguments and types (via `group_streams`).
+///
+/// **Input:** "(p1 : t1, p2: t2, ...)"
+/// **Output:** "(p1, p2, ...)", "(t1, t2, ...)"
pub fn args_divide_grouped(item: &TokenStream) -> (TokenStream, TokenStream) {
let (patterns, types) = args_divide(&item);
(group_streams(&patterns), group_streams(&types))
}
-/// Input: vec![p1, p2, ...]
-/// Output: "(p1, p2, ...)"
+/// Transform a vector of elements into a (TokenTree) group of elements
+///
+/// **Input:** vec!["p1", "p2", ...]
+/// **Output:** "(p1, p2, ...)"
pub fn group_streams(patterns: &Vec<TokenStream>) -> TokenStream {
let mut inner_ts = TokenStream::new();
inner_ts.extend(patterns.clone().into_iter().flat_map(|i| [",".parse().unwrap(), i]).skip(1));