circomspect_program_structure/abstract_syntax_tree/
ast.rs

1use crate::file_definition::FileLocation;
2use num_bigint::BigInt;
3use serde_derive::{Deserialize, Serialize};
4
5pub trait FillMeta {
6    fn fill(&mut self, file_id: usize, elem_id: &mut usize);
7}
8
9pub type MainComponent = (Vec<String>, Expression);
10
11pub fn build_main_component(public: Vec<String>, call: Expression) -> MainComponent {
12    (public, call)
13}
14
15pub type Version = (usize, usize, usize);
16pub type TagList = Vec<String>;
17
18#[derive(Clone)]
19pub struct Include {
20    pub meta: Meta,
21    pub path: String,
22}
23
24pub fn build_include(meta: Meta, path: String) -> Include {
25    Include { meta, path }
26}
27
28#[derive(Clone)]
29pub struct Meta {
30    pub elem_id: usize,
31    pub start: usize,
32    pub end: usize,
33    pub location: FileLocation,
34    pub file_id: Option<usize>,
35    pub component_inference: Option<String>,
36    type_knowledge: TypeKnowledge,
37    memory_knowledge: MemoryKnowledge,
38}
39impl Meta {
40    pub fn new(start: usize, end: usize) -> Meta {
41        Meta {
42            end,
43            start,
44            elem_id: 0,
45            location: start..end,
46            file_id: Option::None,
47            component_inference: None,
48            type_knowledge: TypeKnowledge::default(),
49            memory_knowledge: MemoryKnowledge::default(),
50        }
51    }
52    pub fn change_location(&mut self, location: FileLocation, file_id: Option<usize>) {
53        self.location = location;
54        self.file_id = file_id;
55    }
56    pub fn get_start(&self) -> usize {
57        self.location.start
58    }
59    pub fn get_end(&self) -> usize {
60        self.location.end
61    }
62    pub fn get_file_id(&self) -> usize {
63        if let Option::Some(id) = self.file_id {
64            id
65        } else {
66            panic!("Empty file id accessed")
67        }
68    }
69    pub fn get_memory_knowledge(&self) -> &MemoryKnowledge {
70        &self.memory_knowledge
71    }
72    pub fn get_type_knowledge(&self) -> &TypeKnowledge {
73        &self.type_knowledge
74    }
75    pub fn get_mut_memory_knowledge(&mut self) -> &mut MemoryKnowledge {
76        &mut self.memory_knowledge
77    }
78    pub fn get_mut_type_knowledge(&mut self) -> &mut TypeKnowledge {
79        &mut self.type_knowledge
80    }
81    pub fn file_location(&self) -> FileLocation {
82        self.location.clone()
83    }
84    pub fn set_file_id(&mut self, file_id: usize) {
85        self.file_id = Option::Some(file_id);
86    }
87}
88
89#[derive(Clone)]
90pub struct AST {
91    pub meta: Meta,
92    pub compiler_version: Option<Version>,
93    pub custom_gates: bool,
94    pub custom_gates_declared: bool,
95    pub includes: Vec<Include>,
96    pub definitions: Vec<Definition>,
97    pub main_component: Option<MainComponent>,
98}
99impl AST {
100    pub fn new(
101        meta: Meta,
102        compiler_version: Option<Version>,
103        custom_gates: bool,
104        includes: Vec<Include>,
105        definitions: Vec<Definition>,
106        main_component: Option<MainComponent>,
107    ) -> AST {
108        let custom_gates_declared = definitions.iter().any(|definition| {
109            matches!(definition, Definition::Template { is_custom_gate: true, .. })
110        });
111        AST {
112            meta,
113            compiler_version,
114            custom_gates,
115            custom_gates_declared,
116            includes,
117            definitions,
118            main_component,
119        }
120    }
121}
122
123#[derive(Clone)]
124pub enum Definition {
125    Template {
126        meta: Meta,
127        name: String,
128        args: Vec<String>,
129        arg_location: FileLocation,
130        body: Statement,
131        parallel: bool,
132        is_custom_gate: bool,
133    },
134    Function {
135        meta: Meta,
136        name: String,
137        args: Vec<String>,
138        arg_location: FileLocation,
139        body: Statement,
140    },
141}
142pub fn build_template(
143    meta: Meta,
144    name: String,
145    args: Vec<String>,
146    arg_location: FileLocation,
147    body: Statement,
148    parallel: bool,
149    is_custom_gate: bool,
150) -> Definition {
151    Definition::Template { meta, name, args, arg_location, body, parallel, is_custom_gate }
152}
153
154pub fn build_function(
155    meta: Meta,
156    name: String,
157    args: Vec<String>,
158    arg_location: FileLocation,
159    body: Statement,
160) -> Definition {
161    Definition::Function { meta, name, args, arg_location, body }
162}
163
164impl Definition {
165    pub fn name(&self) -> String {
166        match self {
167            Self::Template { name, .. } => name.clone(),
168            Self::Function { name, .. } => name.clone(),
169        }
170    }
171}
172
173#[derive(Clone)]
174pub enum Statement {
175    IfThenElse {
176        meta: Meta,
177        cond: Expression,
178        if_case: Box<Statement>,
179        else_case: Option<Box<Statement>>,
180    },
181    While {
182        meta: Meta,
183        cond: Expression,
184        stmt: Box<Statement>,
185    },
186    Return {
187        meta: Meta,
188        value: Expression,
189    },
190    InitializationBlock {
191        meta: Meta,
192        xtype: VariableType,
193        initializations: Vec<Statement>,
194    },
195    Declaration {
196        meta: Meta,
197        xtype: VariableType,
198        name: String,
199        dimensions: Vec<Expression>,
200        is_constant: bool,
201    },
202    Substitution {
203        meta: Meta,
204        var: String,
205        access: Vec<Access>,
206        op: AssignOp,
207        rhe: Expression,
208    },
209    MultiSubstitution {
210        meta: Meta,
211        lhe: Expression,
212        op: AssignOp,
213        rhe: Expression,
214    },
215    ConstraintEquality {
216        meta: Meta,
217        lhe: Expression,
218        rhe: Expression,
219    },
220    LogCall {
221        meta: Meta,
222        args: Vec<LogArgument>,
223    },
224    Block {
225        meta: Meta,
226        stmts: Vec<Statement>,
227    },
228    Assert {
229        meta: Meta,
230        arg: Expression,
231    },
232}
233
234#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
235pub enum SignalElementType {
236    Empty,
237    Binary,
238    FieldElement,
239}
240
241#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
242pub enum SignalType {
243    Output,
244    Input,
245    Intermediate,
246}
247
248#[derive(Clone, PartialEq, Ord, PartialOrd, Eq)]
249pub enum VariableType {
250    Var,
251    Signal(SignalType, TagList),
252    Component,
253    AnonymousComponent,
254}
255
256#[derive(Clone)]
257pub enum Expression {
258    InfixOp {
259        meta: Meta,
260        lhe: Box<Expression>,
261        infix_op: ExpressionInfixOpcode,
262        rhe: Box<Expression>,
263    },
264    PrefixOp {
265        meta: Meta,
266        prefix_op: ExpressionPrefixOpcode,
267        rhe: Box<Expression>,
268    },
269    InlineSwitchOp {
270        meta: Meta,
271        cond: Box<Expression>,
272        if_true: Box<Expression>,
273        if_false: Box<Expression>,
274    },
275    ParallelOp {
276        meta: Meta,
277        rhe: Box<Expression>,
278    },
279    Variable {
280        meta: Meta,
281        name: String,
282        access: Vec<Access>,
283    },
284    Number(Meta, BigInt),
285    Call {
286        meta: Meta,
287        id: String,
288        args: Vec<Expression>,
289    },
290    AnonymousComponent {
291        meta: Meta,
292        id: String,
293        is_parallel: bool,
294        params: Vec<Expression>,
295        signals: Vec<Expression>,
296        names: Option<Vec<(AssignOp, String)>>,
297    },
298    // UniformArray is only used internally by Circom for default initialization
299    // of uninitialized arrays.
300    // UniformArray {
301    //     meta: Meta,
302    //     value: Box<Expression>,
303    //     dimension: Box<Expression>,
304    // },
305    ArrayInLine {
306        meta: Meta,
307        values: Vec<Expression>,
308    },
309    Tuple {
310        meta: Meta,
311        values: Vec<Expression>,
312    },
313}
314
315#[derive(Clone)]
316pub enum Access {
317    ComponentAccess(String),
318    ArrayAccess(Expression),
319}
320pub fn build_component_access(acc: String) -> Access {
321    Access::ComponentAccess(acc)
322}
323pub fn build_array_access(expr: Expression) -> Access {
324    Access::ArrayAccess(expr)
325}
326
327#[derive(Copy, Clone, Eq, PartialEq)]
328pub enum AssignOp {
329    AssignVar,
330    AssignSignal,
331    AssignConstraintSignal,
332}
333
334#[derive(Copy, Clone, PartialEq, Eq)]
335pub enum ExpressionInfixOpcode {
336    Mul,
337    Div,
338    Add,
339    Sub,
340    Pow,
341    IntDiv,
342    Mod,
343    ShiftL,
344    ShiftR,
345    LesserEq,
346    GreaterEq,
347    Lesser,
348    Greater,
349    Eq,
350    NotEq,
351    BoolOr,
352    BoolAnd,
353    BitOr,
354    BitAnd,
355    BitXor,
356}
357
358#[derive(Copy, Clone, PartialEq, Eq)]
359pub enum ExpressionPrefixOpcode {
360    Sub,
361    BoolNot,
362    Complement,
363}
364
365#[derive(Clone)]
366pub enum LogArgument {
367    LogStr(String),
368    LogExp(Expression),
369}
370
371pub fn build_log_string(acc: String) -> LogArgument {
372    LogArgument::LogStr(acc)
373}
374
375pub fn build_log_expression(expr: Expression) -> LogArgument {
376    LogArgument::LogExp(expr)
377}
378
379// Knowledge buckets
380#[derive(Copy, Clone, PartialOrd, PartialEq, Ord, Eq)]
381pub enum TypeReduction {
382    Variable,
383    Component,
384    Signal,
385    Tag,
386}
387
388#[derive(Default, Clone)]
389pub struct TypeKnowledge {
390    reduces_to: Option<TypeReduction>,
391}
392impl TypeKnowledge {
393    pub fn new() -> TypeKnowledge {
394        TypeKnowledge::default()
395    }
396    pub fn set_reduces_to(&mut self, reduces_to: TypeReduction) {
397        self.reduces_to = Option::Some(reduces_to);
398    }
399    pub fn reduces_to(&self) -> TypeReduction {
400        if let Option::Some(t) = &self.reduces_to {
401            *t
402        } else {
403            panic!("Type knowledge accessed before it is initialized.");
404        }
405    }
406    pub fn is_var(&self) -> bool {
407        self.reduces_to() == TypeReduction::Variable
408    }
409    pub fn is_component(&self) -> bool {
410        self.reduces_to() == TypeReduction::Component
411    }
412    pub fn is_signal(&self) -> bool {
413        self.reduces_to() == TypeReduction::Signal
414    }
415    pub fn is_tag(&self) -> bool {
416        self.reduces_to() == TypeReduction::Tag
417    }
418}
419
420#[derive(Default, Clone)]
421pub struct MemoryKnowledge {
422    concrete_dimensions: Option<Vec<usize>>,
423    full_length: Option<usize>,
424    abstract_memory_address: Option<usize>,
425}
426impl MemoryKnowledge {
427    pub fn new() -> MemoryKnowledge {
428        MemoryKnowledge::default()
429    }
430    pub fn set_concrete_dimensions(&mut self, value: Vec<usize>) {
431        self.full_length = Option::Some(value.iter().fold(1, |p, v| p * (*v)));
432        self.concrete_dimensions = Option::Some(value);
433    }
434    pub fn set_abstract_memory_address(&mut self, value: usize) {
435        self.abstract_memory_address = Option::Some(value);
436    }
437    pub fn concrete_dimensions(&self) -> &[usize] {
438        if let Option::Some(v) = &self.concrete_dimensions {
439            v
440        } else {
441            panic!("Concrete dimensions accessed before it is initialized.");
442        }
443    }
444    pub fn full_length(&self) -> usize {
445        if let Option::Some(v) = &self.full_length {
446            *v
447        } else {
448            panic!("Full dimension accessed before it is initialized.");
449        }
450    }
451    pub fn abstract_memory_address(&self) -> usize {
452        if let Option::Some(v) = &self.abstract_memory_address {
453            *v
454        } else {
455            panic!("Abstract memory address accessed before it is initialized.");
456        }
457    }
458}