1use crate::{file_definition::{FileLocation, FileID}, error_definition::Report, error_code::{ReportCode}};
2use num_bigint::BigInt;
3use serde_derive::{Deserialize, Serialize};
4
5#[derive(Clone)]
6pub enum Pragma {
7 Version(Meta, FileID, Version),
8 CustomGates(Meta ,FileID),
9 Unrecognized,
10}
11
12pub trait FillMeta {
13 fn fill(&mut self, file_id: usize, elem_id: &mut usize);
14}
15
16pub type MainComponent = (Vec<String>, Expression);
17pub fn build_main_component(public: Vec<String>, call: Expression) -> MainComponent {
18 (public, call)
19}
20
21pub type Version = (usize, usize, usize);
22
23#[derive(Clone)]
24pub struct Meta {
25 pub elem_id: usize,
26 pub start: usize,
27 pub end: usize,
28 pub location: FileLocation,
29 pub file_id: Option<usize>,
30 pub component_inference: Option<String>,
31 type_knowledge: TypeKnowledge,
32 memory_knowledge: MemoryKnowledge,
33}
34impl Meta {
35 pub fn new(start: usize, end: usize) -> Meta {
36 Meta {
37 end,
38 start,
39 elem_id: 0,
40 location: start..end,
41 file_id: Option::None,
42 component_inference: None,
43 type_knowledge: TypeKnowledge::default(),
44 memory_knowledge: MemoryKnowledge::default(),
45 }
46 }
47 pub fn change_location(&mut self, location: FileLocation, file_id: Option<usize>) {
48 self.location = location;
49 self.file_id = file_id;
50 }
51 pub fn get_start(&self) -> usize {
52 self.location.start
53 }
54 pub fn get_end(&self) -> usize {
55 self.location.end
56 }
57 pub fn get_file_id(&self) -> usize {
58 if let Option::Some(id) = self.file_id {
59 id
60 } else {
61 panic!("Empty file id accessed")
62 }
63 }
64 pub fn get_memory_knowledge(&self) -> &MemoryKnowledge {
65 &self.memory_knowledge
66 }
67 pub fn get_type_knowledge(&self) -> &TypeKnowledge {
68 &self.type_knowledge
69 }
70 pub fn get_mut_memory_knowledge(&mut self) -> &mut MemoryKnowledge {
71 &mut self.memory_knowledge
72 }
73 pub fn get_mut_type_knowledge(&mut self) -> &mut TypeKnowledge {
74 &mut self.type_knowledge
75 }
76 pub fn file_location(&self) -> FileLocation {
77 self.location.clone()
78 }
79 pub fn set_file_id(&mut self, file_id: usize) {
80 self.file_id = Option::Some(file_id);
81 }
82}
83
84#[derive(Clone)]
85pub struct AST {
86 pub meta: Meta,
87 pub compiler_version: Option<Version>,
88 pub custom_gates: bool,
89 pub custom_gates_declared: bool,
90 pub includes: Vec<String>,
91 pub definitions: Vec<Definition>,
92 pub main_component: Option<MainComponent>,
93}
94
95impl AST {
96 pub fn new(
97 meta: Meta,
98 pragmas: Vec<Pragma>,
99 includes: Vec<String>,
100 definitions: Vec<Definition>,
101 main_component: Option<MainComponent>,
102 ) -> (AST,Vec<Report>) {
103 let mut custom_gates = None;
104 let mut compiler_version = None;
105 let mut reports = Vec::new();
106 for p in pragmas {
107 match p {
108 Pragma::Version(location, file_id, ver) => match compiler_version {
110 Some(_) => reports.push(produce_report(
111 ReportCode::MultiplePragma,location.start..location.end, file_id)),
112 None => compiler_version = Some(ver),
113 },
114 Pragma::CustomGates(location, file_id ) => match custom_gates {
115 Some(_) => reports.push(produce_report(
116 ReportCode::MultiplePragma, location.start..location.end, file_id)),
117 None => custom_gates = Some(true),
118 },
119 Pragma::Unrecognized => {}, }
122 }
123
124 let custom_gates_declared = definitions.iter().any(|definition| {
125 matches!(definition, Definition::Template { is_custom_gate: true, .. })
126 });
127
128 (AST {
129 meta,
130 compiler_version,
131 custom_gates: custom_gates.unwrap_or(false),
132 custom_gates_declared,
133 includes,
134 definitions,
135 main_component,
136 }, reports)
137 }
138}
139
140#[derive(Clone)]
141pub enum Definition {
142 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 },
151 Function {
152 meta: Meta,
153 name: String,
154 args: Vec<String>,
155 arg_location: FileLocation,
156 body: Statement,
157 },
158}
159pub fn build_template(
160 meta: Meta,
161 name: String,
162 args: Vec<String>,
163 arg_location: FileLocation,
164 body: Statement,
165 parallel: bool,
166 is_custom_gate: bool,
167) -> Definition {
168 Definition::Template { meta, name, args, arg_location, body, parallel, is_custom_gate }
169}
170
171pub fn build_function(
172 meta: Meta,
173 name: String,
174 args: Vec<String>,
175 arg_location: FileLocation,
176 body: Statement,
177) -> Definition {
178 Definition::Function { meta, name, args, arg_location, body }
179}
180
181#[derive(Clone)]
182pub enum Statement {
183 IfThenElse {
184 meta: Meta,
185 cond: Expression,
186 if_case: Box<Statement>,
187 else_case: Option<Box<Statement>>,
188 },
189 While {
190 meta: Meta,
191 cond: Expression,
192 stmt: Box<Statement>,
193 },
194 Return {
195 meta: Meta,
196 value: Expression,
197 },
198 InitializationBlock {
199 meta: Meta,
200 xtype: VariableType,
201 initializations: Vec<Statement>,
202 },
203 Declaration {
204 meta: Meta,
205 xtype: VariableType,
206 name: String,
207 dimensions: Vec<Expression>,
208 is_constant: bool,
209 },
210 Substitution {
211 meta: Meta,
212 var: String,
213 access: Vec<Access>,
214 op: AssignOp,
215 rhe: Expression,
216 },
217 MultSubstitution {
218 meta: Meta,
219 lhe: Expression,
220 op: AssignOp,
221 rhe: Expression,
222 },
223 UnderscoreSubstitution{
224 meta: Meta,
225 op: AssignOp,
226 rhe: Expression,
227 },
228 ConstraintEquality {
229 meta: Meta,
230 lhe: Expression,
231 rhe: Expression,
232 },
233 LogCall {
234 meta: Meta,
235 args: Vec<LogArgument>,
236 },
237 Block {
238 meta: Meta,
239 stmts: Vec<Statement>,
240 },
241 Assert {
242 meta: Meta,
243 arg: Expression,
244 },
245}
246
247#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
248pub enum SignalType {
249 Output,
250 Input,
251 Intermediate,
252}
253
254pub type TagList = Vec<String>;
255
256#[derive(Clone, PartialEq, Ord, PartialOrd, Eq)]
257pub enum VariableType {
258 Var,
259 Signal(SignalType, TagList),
260 Component,
261 AnonymousComponent,
262}
263
264#[derive(Clone)]
265pub enum Expression {
266 InfixOp {
267 meta: Meta,
268 lhe: Box<Expression>,
269 infix_op: ExpressionInfixOpcode,
270 rhe: Box<Expression>,
271 },
272 PrefixOp {
273 meta: Meta,
274 prefix_op: ExpressionPrefixOpcode,
275 rhe: Box<Expression>,
276 },
277 InlineSwitchOp {
278 meta: Meta,
279 cond: Box<Expression>,
280 if_true: Box<Expression>,
281 if_false: Box<Expression>,
282 },
283 ParallelOp {
284 meta: Meta,
285 rhe: Box<Expression>,
286 },
287 Variable {
288 meta: Meta,
289 name: String,
290 access: Vec<Access>,
291 },
292 Number(Meta, BigInt),
293 Call {
294 meta: Meta,
295 id: String,
296 args: Vec<Expression>,
297 },
298 AnonymousComp{
299 meta: Meta,
300 id: String,
301 is_parallel: bool,
302 params: Vec<Expression>,
303 signals: Vec<Expression>,
304 names: Option<Vec<(AssignOp,String)>>,
305 },
306 ArrayInLine {
307 meta: Meta,
308 values: Vec<Expression>,
309 },
310 Tuple {
311 meta: Meta,
312 values: Vec<Expression>,
313 },
314 UniformArray {
315 meta: Meta,
316 value: Box<Expression>,
317 dimension: Box<Expression>,
318 },
319}
320
321#[derive(Clone)]
322pub enum Access {
323 ComponentAccess(String),
324 ArrayAccess(Expression),
325}
326pub fn build_component_access(acc: String) -> Access {
327 Access::ComponentAccess(acc)
328}
329pub fn build_array_access(expr: Expression) -> Access {
330 Access::ArrayAccess(expr)
331}
332
333#[derive(Copy, Clone, Eq, PartialEq)]
334pub enum AssignOp {
335 AssignVar,
336 AssignSignal,
337 AssignConstraintSignal,
338}
339
340#[derive(Copy, Clone, PartialEq)]
341pub enum ExpressionInfixOpcode {
342 Mul,
343 Div,
344 Add,
345 Sub,
346 Pow,
347 IntDiv,
348 Mod,
349 ShiftL,
350 ShiftR,
351 LesserEq,
352 GreaterEq,
353 Lesser,
354 Greater,
355 Eq,
356 NotEq,
357 BoolOr,
358 BoolAnd,
359 BitOr,
360 BitAnd,
361 BitXor,
362}
363
364#[derive(Copy, Clone, PartialEq)]
365pub enum ExpressionPrefixOpcode {
366 Sub,
367 BoolNot,
368 Complement,
369}
370
371#[derive(Copy, Clone, PartialOrd, PartialEq, Ord, Eq)]
374pub enum TypeReduction {
375 Variable,
376 Component,
377 Signal,
378 Tag,
379}
380
381#[derive(Clone)]
382pub enum LogArgument {
383 LogStr(String),
384 LogExp(Expression),
385}
386pub fn build_log_string(acc: String) -> LogArgument {
387 LogArgument::LogStr(acc)
388}
389pub fn build_log_expression(expr: Expression) -> LogArgument {
390 LogArgument::LogExp(expr)
391}
392
393
394#[derive(Default, Clone)]
395pub struct TypeKnowledge {
396 reduces_to: Option<TypeReduction>,
397}
398impl TypeKnowledge {
399 pub fn new() -> TypeKnowledge {
400 TypeKnowledge::default()
401 }
402 pub fn set_reduces_to(&mut self, reduces_to: TypeReduction) {
403 self.reduces_to = Option::Some(reduces_to);
404 }
405 pub fn get_reduces_to(&self) -> TypeReduction {
406 if let Option::Some(t) = &self.reduces_to {
407 *t
408 } else {
409 panic!("reduces_to knowledge is been look at without being initialized");
410 }
411 }
412 pub fn is_var(&self) -> bool {
413 self.get_reduces_to() == TypeReduction::Variable
414 }
415 pub fn is_component(&self) -> bool {
416 self.get_reduces_to() == TypeReduction::Component
417 }
418 pub fn is_signal(&self) -> bool {
419 self.get_reduces_to() == TypeReduction::Signal
420 }
421 pub fn is_tag(&self) -> bool {
422 self.get_reduces_to() == TypeReduction::Tag
423 }
424}
425
426#[derive(Default, Clone)]
427pub struct MemoryKnowledge {
428 concrete_dimensions: Option<Vec<usize>>,
429 full_length: Option<usize>,
430 abstract_memory_address: Option<usize>,
431}
432impl MemoryKnowledge {
433 pub fn new() -> MemoryKnowledge {
434 MemoryKnowledge::default()
435 }
436 pub fn set_concrete_dimensions(&mut self, value: Vec<usize>) {
437 self.full_length = Option::Some(value.iter().fold(1, |p, v| p * (*v)));
438 self.concrete_dimensions = Option::Some(value);
439 }
440 pub fn set_abstract_memory_address(&mut self, value: usize) {
441 self.abstract_memory_address = Option::Some(value);
442 }
443 pub fn get_concrete_dimensions(&self) -> &[usize] {
444 if let Option::Some(v) = &self.concrete_dimensions {
445 v
446 } else {
447 panic!("concrete dimensions was look at without being initialized");
448 }
449 }
450 pub fn get_full_length(&self) -> usize {
451 if let Option::Some(v) = &self.full_length {
452 *v
453 } else {
454 panic!("full dimension was look at without being initialized");
455 }
456 }
457 pub fn get_abstract_memory_address(&self) -> usize {
458 if let Option::Some(v) = &self.abstract_memory_address {
459 *v
460 } else {
461 panic!("abstract memory address was look at without being initialized");
462 }
463 }
464}
465
466 pub fn produce_report(error_code: ReportCode, location : FileLocation, file_id : FileID) -> Report {
467 use ReportCode::*;
468 let report = match error_code {
469 UnclosedComment => {
470 let mut report =
471 Report::error("unterminated /* */".to_string(), ReportCode::UnclosedComment);
472 report.add_primary(location, file_id, "Comment starts here".to_string());
473 report
474 }
475 NoMainFoundInProject => Report::error(
476 "No main specified in the project structure".to_string(),
477 ReportCode::NoMainFoundInProject,
478 ),
479 MultipleMain =>{
480 Report::error(
481 "Multiple main components in the project structure".to_string(),
482 ReportCode::MultipleMain,
483 )
484 }
485 MissingSemicolon => {
486 let mut report = Report::error(format!("Missing semicolon"),
487 ReportCode::MissingSemicolon);
488 report.add_primary(location, file_id, "A semicolon is needed here".to_string());
489 report
490 }
491 UnrecognizedInclude => {
492 let mut report =
493 Report::error("unrecognized argument in include directive".to_string(), ReportCode::UnrecognizedInclude);
494 report.add_primary(location, file_id, "this argument".to_string());
495 report
496
497 }
498 UnrecognizedPragma => {
499 let mut report =
500 Report::error("unrecognized argument in pragma directive".to_string(), ReportCode::UnrecognizedPragma);
501 report.add_primary(location, file_id, "this argument".to_string());
502 report
503
504 }
505 UnrecognizedVersion => {
506 let mut report =
507 Report::error("unrecognized version argument in pragma directive".to_string(), ReportCode::UnrecognizedVersion);
508 report.add_primary(location, file_id, "this argument".to_string());
509 report
510 }
511 IllegalExpression => {
512 let mut report =
513 Report::error("illegal expression".to_string(), ReportCode::IllegalExpression);
514 report.add_primary(location, file_id, "here".to_string());
515 report
516 }
517 MultiplePragma => {
518 let mut report =
519 Report::error("Multiple pragma directives".to_string(), ReportCode::MultiplePragma);
520 report.add_primary(location, file_id, "here".to_string());
521 report
522 },
523 ExpectedIdentifier => {
524 let mut report =
525 Report::error("An identifier is expected".to_string(), ReportCode::ExpectedIdentifier);
526 report.add_primary(location, file_id, "This should be an identifier".to_string());
527 report
528 },
529 _ => unreachable!(),
530 };
531 report
532}
533
534pub fn produce_version_warning_report(path : String, version : Version) -> Report {
535 let mut r = Report::warning(
536 format!(
537 "File {} does not include pragma version. Assuming pragma version {:?}",
538 path, version
539 ),
540 ReportCode::NoCompilerVersionWarning,
541 );
542 r.add_note(format!("At the beginning of file {}, you should add the directive \"pragma circom <Version>\", to indicate which compiler version you are using.",path));
543 r
544}
545
546
547pub fn produce_report_with_message(error_code : ReportCode, msg : String) -> Report {
548 match error_code {
549 ReportCode::FileOs => {
550 Report::error(
551 format!("Could not open file {}", msg),
552 ReportCode::FileOs,
553 )
554 }
555 ReportCode::IncludeNotFound => {
556 let mut r = Report::error(
557 format!(" The file {} to be included has not been found", msg),
558 ReportCode::IncludeNotFound,
559 );
560 r.add_note("Consider using compilation option -l to indicate include paths".to_string());
561 r
562 },
563 _ => unreachable!()
564 }
565}
566
567pub fn produce_compiler_version_report(path : String, required_version : Version, version : Version) -> Report {
568 let report = Report::error(
569 format!("File {} requires pragma version {:?} that is not supported by the compiler (version {:?})", path, required_version, version ),
570 ReportCode::CompilerVersionError,
571 );
572 report
573}
574
575pub fn anonymous_inside_condition_error(meta : Meta) -> Report {
576 let msg = "An anonymous component cannot be used inside a condition ".to_string();
577 let mut report = Report::error(
578 format!("{}", msg),
579 ReportCode::AnonymousCompError,
580 );
581 let file_id = meta.get_file_id().clone();
582 report.add_primary(
583 meta.location,
584 file_id,
585 "This is an anonymous component used inside a condition".to_string(),
586 );
587 report
588}
589
590pub fn anonymous_general_error(meta : Meta, msg : String) -> Report {
591 let mut report = Report::error(
592 format!("{}", msg),
593 ReportCode::AnonymousCompError,
594 );
595 let file_id = meta.get_file_id().clone();
596 report.add_primary(
597 meta.location,
598 file_id,
599 "This is the anonymous component whose use is not allowed".to_string(),
600 );
601 report
602}
603
604pub fn tuple_general_error(meta : Meta, msg : String) -> Report {
605 let mut report = Report::error(
606 format!("{}", msg),
607 ReportCode::TupleError,
608 );
609 let file_id = meta.get_file_id().clone();
610 report.add_primary(
611 meta.location,
612 file_id,
613 "This is the tuple whose use is not allowed".to_string(),
614 );
615 report
616}