Skip to main content

sigma_compiler_core/
syntax.rs

1//! A module for parsing the syntax of the macro
2
3use super::sigma::combiners::StatementTree;
4use super::sigma::types::*;
5use quote::format_ident;
6use std::collections::HashMap;
7use std::fmt;
8use syn::ext::IdentExt;
9use syn::parse::{Parse, ParseStream, Result};
10use syn::punctuated::Punctuated;
11use syn::{parenthesized, Error, Expr, Ident, Token};
12
13/// A [`TaggedScalar`] is an [`struct@Ident`] representing a `Scalar`,
14/// preceded by zero or more of the following tags: `pub`, `rand`, `vec`
15///
16/// The following combinations are valid:
17///  - (nothing)
18///  - `pub`
19///  - `rand`
20///  - `vec`
21///  - `pub vec`
22///  - `rand vec`
23
24#[derive(Clone, Debug, PartialEq, Eq)]
25pub struct TaggedScalar {
26    pub id: Ident,
27    pub is_pub: bool,
28    pub is_rand: bool,
29    pub is_vec: bool,
30}
31
32impl Parse for TaggedScalar {
33    fn parse(input: ParseStream) -> Result<Self> {
34        let (mut is_pub, mut is_rand, mut is_vec) = (false, false, false);
35        loop {
36            let id = input.call(Ident::parse_any)?;
37            match id.to_string().as_str() {
38                // pub and rand are mutually exclusive
39                "pub" if !is_rand => {
40                    is_pub = true;
41                }
42                "rand" if !is_pub => {
43                    is_rand = true;
44                }
45                // any other use of the tagging keywords is not allowed
46                "pub" | "rand" | "cind" | "const" => {
47                    return Err(Error::new(id.span(), "tag not allowed in this position"));
48                }
49                // vec is allowed with any other tag
50                "vec" => {
51                    is_vec = true;
52                }
53                _ => {
54                    return Ok(TaggedScalar {
55                        id,
56                        is_pub,
57                        is_rand,
58                        is_vec,
59                    });
60                }
61            }
62        }
63    }
64}
65
66impl fmt::Display for TaggedScalar {
67    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68        let mut res = String::new();
69        if self.is_pub {
70            res += "pub ";
71        }
72        if self.is_rand {
73            res += "rand ";
74        }
75        if self.is_vec {
76            res += "vec ";
77        }
78        res += &self.id.to_string();
79
80        write!(f, "{res}")
81    }
82}
83
84/// A [`TaggedPoint`] is an [`struct@Ident`] representing a `Point`,
85/// preceded by zero or more of the following tags: `cind`, `const`,
86/// `vec`
87///
88/// All combinations are valid:
89///  - (nothing)
90///  - `cind`
91///  - `const`
92///  - `cind const`
93///  - `vec`
94///  - `cind vec`
95///  - `const vec`
96///  - `cind const vec`
97
98#[derive(Clone, Debug, PartialEq, Eq)]
99pub struct TaggedPoint {
100    pub id: Ident,
101    pub is_cind: bool,
102    pub is_const: bool,
103    pub is_vec: bool,
104}
105
106impl Parse for TaggedPoint {
107    fn parse(input: ParseStream) -> Result<Self> {
108        // Points are always pub
109        let (mut is_cind, mut is_const, mut is_vec) = (false, false, false);
110        loop {
111            let id = input.call(Ident::parse_any)?;
112            match id.to_string().as_str() {
113                "cind" => {
114                    is_cind = true;
115                }
116                "const" => {
117                    is_const = true;
118                }
119                // any other use of the tagging keywords is not allowed
120                "pub" | "rand" => {
121                    return Err(Error::new(id.span(), "tag not allowed in this position"));
122                }
123                "vec" => {
124                    is_vec = true;
125                }
126                _ => {
127                    return Ok(TaggedPoint {
128                        id,
129                        is_cind,
130                        is_const,
131                        is_vec,
132                    });
133                }
134            }
135        }
136    }
137}
138
139impl fmt::Display for TaggedPoint {
140    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141        let mut res = String::new();
142        if self.is_vec {
143            res += "vec ";
144        }
145        if self.is_const {
146            res += "const ";
147        }
148        if self.is_cind {
149            res += "cind ";
150        }
151        res += &self.id.to_string();
152
153        write!(f, "{res}")
154    }
155}
156
157/// A [`TaggedIdent`] can be either a [`TaggedScalar`] or a
158/// [`TaggedPoint`]
159#[derive(Clone, Debug, PartialEq, Eq)]
160pub enum TaggedIdent {
161    Scalar(TaggedScalar),
162    Point(TaggedPoint),
163}
164
165/// Convert a [`TaggedIdent`] to its underlying [`AExprType`]
166impl From<&TaggedIdent> for AExprType {
167    fn from(ti: &TaggedIdent) -> Self {
168        match ti {
169            TaggedIdent::Scalar(ts) => Self::Scalar {
170                is_pub: ts.is_pub,
171                is_vec: ts.is_vec,
172                val: None,
173            },
174            TaggedIdent::Point(tp) => Self::Point {
175                is_pub: true,
176                is_vec: tp.is_vec,
177            },
178        }
179    }
180}
181
182/// A [`TaggedVarDict`] is a dictionary of the available variables,
183/// mapping the string version of [`struct@Ident`]s to [`TaggedIdent`],
184/// which includes their type ([`Scalar`](TaggedIdent::Scalar) or
185/// [`Point`](TaggedIdent::Point))
186pub type TaggedVarDict = HashMap<String, TaggedIdent>;
187
188/// Convert a [`TaggedVarDict`] (a map from [`String`] to
189/// [`TaggedIdent`]) into the equivalent [`VarDict`] (a map from
190/// [`String`] to [`AExprType`])
191pub fn taggedvardict_to_vardict(vd: &TaggedVarDict) -> VarDict {
192    vd.iter()
193        .map(|(k, v)| (k.clone(), AExprType::from(v)))
194        .collect()
195}
196
197/// Collect the list of [`Point`](TaggedIdent::Point)s tagged `cind`
198/// from the given [`TaggedVarDict`]
199pub fn collect_cind_points(vars: &TaggedVarDict) -> Vec<Ident> {
200    let mut cind_points: Vec<Ident> = vars
201        .values()
202        .filter_map(|ti| {
203            if let TaggedIdent::Point(TaggedPoint {
204                is_cind: true,
205                is_vec: false,
206                id,
207                ..
208            }) = ti
209            {
210                Some(id.clone())
211            } else {
212                None
213            }
214        })
215        .collect();
216    cind_points.sort();
217    cind_points
218}
219
220#[cfg(test)]
221/// Convert a list of strings describing `Scalar`s and a list of strings
222/// describing `Point`s into a [`TaggedVarDict`]
223pub fn taggedvardict_from_strs((scalar_strs, point_strs): (&[&str], &[&str])) -> TaggedVarDict {
224    let mut vars = HashMap::new();
225
226    for scalar in scalar_strs {
227        let ts: TaggedScalar = syn::parse_str(scalar).unwrap();
228        vars.insert(ts.id.to_string(), TaggedIdent::Scalar(ts));
229    }
230    for point in point_strs {
231        let tp: TaggedPoint = syn::parse_str(point).unwrap();
232        vars.insert(tp.id.to_string(), TaggedIdent::Point(tp));
233    }
234    vars
235}
236
237/// The [`SigmaCompSpec`] struct is the result of parsing the macro
238/// input.
239#[derive(Debug)]
240pub struct SigmaCompSpec {
241    /// An identifier for the name of the zero-knowledge protocol being
242    /// defined
243    pub proto_name: Ident,
244
245    /// An identifier for the mathematical
246    /// [`PrimeGroup`](https://docs.rs/group/0.13.0/group/prime/trait.PrimeGroup.html)
247    /// being used (if none is specified, it is assumed there is a
248    /// default type called `G` in scope that implements the
249    /// [`PrimeGroup`](https://docs.rs/group/0.13.0/group/prime/trait.PrimeGroup.html)
250    /// trait)
251    pub group_name: Ident,
252
253    /// A [`TaggedVarDict`] mapping variable names to their types
254    /// (`Scalar` or `Point`) and tags (e.g., `rand`, `pub`, `vec`,
255    /// `cind`, `const`)
256    pub vars: TaggedVarDict,
257
258    /// A [`StatementTree`] representing the statements provided in the
259    /// macro invocation that are to be proved true in zero knowledge
260    pub statements: StatementTree,
261}
262
263// T is TaggedScalar or TaggedPoint
264fn paren_taggedidents<T: Parse>(input: ParseStream) -> Result<Vec<T>> {
265    let content;
266    parenthesized!(content in input);
267    let punc: Punctuated<T, Token![,]> = content.parse_terminated(T::parse, Token![,])?;
268    Ok(punc.into_iter().collect())
269}
270
271impl Parse for SigmaCompSpec {
272    fn parse(input: ParseStream) -> Result<Self> {
273        let proto_name: Ident = input.parse()?;
274        // See if a group was specified
275        let group_name = if input.peek(Token![<]) {
276            input.parse::<Token![<]>()?;
277            let gr: Ident = input.parse()?;
278            input.parse::<Token![>]>()?;
279            gr
280        } else {
281            format_ident!("G")
282        };
283        input.parse::<Token![,]>()?;
284
285        let mut vars: TaggedVarDict = HashMap::new();
286
287        let scalars = paren_taggedidents::<TaggedScalar>(input)?;
288        vars.extend(
289            scalars
290                .into_iter()
291                .map(|ts| (ts.id.to_string(), TaggedIdent::Scalar(ts))),
292        );
293        input.parse::<Token![,]>()?;
294
295        let points = paren_taggedidents::<TaggedPoint>(input)?;
296        vars.extend(
297            points
298                .into_iter()
299                .map(|tp| (tp.id.to_string(), TaggedIdent::Point(tp))),
300        );
301        input.parse::<Token![,]>()?;
302
303        let statementpunc: Punctuated<Expr, Token![,]> =
304            input.parse_terminated(Expr::parse, Token![,])?;
305        let statementlist: Vec<Expr> = statementpunc.into_iter().collect();
306        let statements = StatementTree::parse_andlist(&statementlist)?;
307
308        Ok(SigmaCompSpec {
309            proto_name,
310            group_name,
311            vars,
312            statements,
313        })
314    }
315}