circom/src/gencode.js
src/gencode.jsRaw
1/*
2 Copyright 2018 0KIMS association.
3
4 This file is part of circom (Zero Knowledge Circuit Compiler).
5
6 circom is a free software: you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 circom is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
14 License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with circom. If not, see <https://www.gnu.org/licenses/>.
18*/
19
20const bigInt = require("big-integer");
21
22module.exports = gen;
23
24function ident(text) {
25 let lines = text.split("\n");
26 for (let i=0; i<lines.length; i++) {
27 if (lines[i]) lines[i] = " "+lines[i];
28 }
29 return lines.join("\n");
30}
31
32function gen(ctx, ast) {
33 if ((ast.type == "NUMBER") ) {
34 return genNumber(ctx, ast);
35 } else if (ast.type == "VARIABLE") {
36 return genVariable(ctx, ast);
37 } else if (ast.type == "PIN") {
38 return genPin(ctx, ast);
39 } else if (ast.type == "OP") {
40 if (ast.op == "=") {
41 return genVarAssignement(ctx, ast);
42 } else if (ast.op == "<--") {
43 return genVarAssignement(ctx, ast);
44 } else if (ast.op == "<==") {
45 return genSignalAssignConstrain(ctx, ast);
46 } else if (ast.op == "===") {
47 return genConstrain(ctx, ast);
48 } else if (ast.op == "+=") {
49 return genVarAddAssignement(ctx, ast);
50 } else if (ast.op == "*=") {
51 return genVarMulAssignement(ctx, ast);
52 } else if (ast.op == "+") {
53 return genAdd(ctx, ast);
54 } else if (ast.op == "-") {
55 return genSub(ctx, ast);
56 } else if (ast.op == "UMINUS") {
57 return genUMinus(ctx, ast);
58 } else if (ast.op == "*") {
59 return genMul(ctx, ast);
60 } else if (ast.op == "%") {
61 return genMod(ctx, ast);
62 } else if (ast.op == "PLUSPLUSRIGHT") {
63 return genPlusPlusRight(ctx, ast);
64 } else if (ast.op == "PLUSPLUSLEFT") {
65 return genPlusPlusLeft(ctx, ast);
66 } else if (ast.op == "MINUSMINUSRIGHT") {
67 return genMinusMinusRight(ctx, ast);
68 } else if (ast.op == "MINUSMINUSLEFT") {
69 return genMinusMinusLeft(ctx, ast);
70 } else if (ast.op == "**") {
71 return genExp(ctx, ast);
72 } else if (ast.op == "/") {
73 return genDiv(ctx, ast);
74 } else if (ast.op == "\\") {
75 return genIDiv(ctx, ast);
76 } else if (ast.op == "&") {
77 return genBAnd(ctx, ast);
78 } else if (ast.op == "&&") {
79 return genAnd(ctx, ast);
80 } else if (ast.op == "||") {
81 return genOr(ctx, ast);
82 } else if (ast.op == "<<") {
83 return genShl(ctx, ast);
84 } else if (ast.op == ">>") {
85 return genShr(ctx, ast);
86 } else if (ast.op == "<") {
87 return genLt(ctx, ast);
88 } else if (ast.op == ">") {
89 return genGt(ctx, ast);
90 } else if (ast.op == "<=") {
91 return genLte(ctx, ast);
92 } else if (ast.op == ">=") {
93 return genGte(ctx, ast);
94 } else if (ast.op == "==") {
95 return genEq(ctx, ast);
96 } else if (ast.op == "!=") {
97 return genNeq(ctx, ast);
98 } else if (ast.op == "?") {
99 return genTerCon(ctx, ast);
100 } else {
101 error(ctx, ast, "Invalid operation: " + ast.op);
102 }
103 } else if (ast.type == "DECLARE") {
104 if (ast.declareType == "COMPONENT") {
105 return genDeclareComponent(ctx, ast);
106 } else if ((ast.declareType == "SIGNALIN")||
107 (ast.declareType == "SIGNALOUT")||
108 (ast.declareType == "SIGNAL")) {
109 return genDeclareSignal(ctx, ast);
110 } else if (ast.declareType == "VARIABLE") {
111 return genDeclareVariable(ctx, ast);
112 } else {
113 error(ctx, ast, "Invalid declaration: " + ast.declareType);
114 }
115 } else if (ast.type == "FUNCTIONCALL") {
116 return genFunctionCall(ctx, ast);
117 } else if (ast.type == "BLOCK") {
118 return genBlock(ctx, ast);
119 } else if (ast.type == "COMPUTE") {
120 return genCompute(ctx, ast);
121 } else if (ast.type == "FOR") {
122 return genFor(ctx, ast);
123 } else if (ast.type == "WHILE") {
124 return genWhile(ctx, ast);
125 } else if (ast.type == "IF") {
126 return genIf(ctx, ast);
127 } else if (ast.type == "RETURN") {
128 return genReturn(ctx, ast);
129 } else if (ast.type == "TEMPLATEDEF") {
130 return genTemplateDef(ctx, ast);
131 } else if (ast.type == "FUNCTIONDEF") {
132 return genFunctionDef(ctx, ast);
133 } else if (ast.type == "INCLUDE") {
134 return genInclude(ctx, ast);
135 } else if (ast.type == "ARRAY") {
136 return genArray(ctx, ast);
137 } else {
138 error(ctx, ast, "GEN -> Invalid AST node type: " + ast.type);
139 }
140}
141
142
143function error(ctx, ast, errStr) {
144 ctx.error = {
145 pos: {
146 first_line: ast.first_line,
147 first_column: ast.first_column,
148 last_line: ast.last_line,
149 last_column: ast.last_column
150 },
151 errStr: errStr,
152 ast: ast,
153 message: errStr
154 };
155}
156
157
158function getScope(ctx, name) {
159 for (let i=ctx.scopes.length-1; i>=0; i--) {
160 if (ctx.scopes[i][name]) return ctx.scopes[i][name];
161 }
162 return null;
163}
164
165
166function genFunctionCall(ctx, ast) {
167 let S = "[";
168 for (let i=0; i<ast.params.length; i++) {
169 if (i>0) S += ",";
170 S += gen(ctx, ast.params[i]);
171 }
172 S+="]";
173
174 return `ctx.callFunction("${ast.name}", ${S})`;
175}
176
177function genBlock(ctx, ast) {
178 let body = "";
179 for (let i=0; i<ast.statements.length; i++) {
180 const l = gen(ctx, ast.statements[i]);
181 if (ctx.error) return;
182 if (l) {
183 body += l;
184 if (body[body.length-1] != "\n") body += ";\n";
185 }
186 }
187 return "{\n"+ident(body)+"}\n";
188}
189
190function genTemplateDef(ctx, ast) {
191 let S = "function(ctx) ";
192
193 const newScope = {};
194 for (let i=0; i< ast.params.length; i++) {
195 newScope[ast.params[i]] = { type: "VARIABLE" };
196 }
197
198 ctx.scopes.push(newScope);
199 S += genBlock(ctx, ast.block);
200 ctx.scopes.pop();
201
202// const scope = ctx.scopes[ctx.scopes.length-1];
203 const scope = ctx.scopes[0]; // Scope for templates is top
204
205 scope[ast.name] = {
206 type: "TEMPLATE"
207 };
208
209 ctx.templates[ast.name] = S;
210 return "";
211}
212
213function genFunctionDef(ctx, ast) {
214 let S = "function(ctx) ";
215
216 const newScope = {};
217 const params = [];
218 for (let i=0; i< ast.params.length; i++) {
219 newScope[ast.params[i]] = { type: "VARIABLE" };
220 params.push(ast.params[i]);
221 }
222
223 ctx.scopes.push(newScope);
224 S += genBlock(ctx, ast.block);
225 ctx.scopes.pop();
226
227// const scope = ctx.scopes[0]; // Scope for templates is top
228 const scope = ctx.scopes[ctx.scopes.length-1];
229
230 scope[ast.name] = {
231 type: "FUNCTION"
232 };
233
234 ctx.functions[ast.name] = S;
235 ctx.functionParams[ast.name] = params;
236 return "";
237}
238
239function genFor(ctx, ast) {
240 ctx.scopes.push({});
241 const init = gen(ctx, ast.init);
242 if (ctx.error) return;
243 const condition = gen(ctx, ast.condition);
244 if (ctx.error) return;
245 const step = gen(ctx, ast.step);
246 if (ctx.error) return;
247 const body = gen(ctx, ast.body);
248 if (ctx.error) return;
249 ctx.scopes.pop();
250 return `for (${init};bigInt(${condition}).neq(bigInt(0));${step}) { \n${body}\n }\n`;
251}
252
253function genWhile(ctx, ast) {
254 const condition = gen(ctx, ast.condition);
255 if (ctx.error) return;
256 const body = gen(ctx, ast.body);
257 if (ctx.error) return;
258 return `while (bigInt(${condition}).neq(bigInt(0))) {\n${body}\n}\n`;
259}
260
261function genCompute(ctx, ast) {
262 const body = gen(ctx, ast.body);
263 if (ctx.error) return;
264 return `{\n${body}\n}\n`;
265}
266
267function genIf(ctx, ast) {
268 const condition = gen(ctx, ast.condition);
269 if (ctx.error) return;
270 const thenBody = gen(ctx, ast.then);
271 if (ctx.error) return;
272 if (ast.else) {
273 const elseBody = gen(ctx, ast.else);
274 if (ctx.error) return;
275 return `if (bigInt(${condition}).neq(bigInt(0))) {\n${thenBody}\n} else {\n${elseBody}\n}\n`;
276 } else {
277 return `if (bigInt(${condition}).neq(bigInt(0))) {\n${thenBody}\n}\n`;
278 }
279}
280
281
282function genReturn(ctx, ast) {
283 const value = gen(ctx, ast.value);
284 if (ctx.error) return;
285 return `return ${value};`;
286}
287
288function genDeclareComponent(ctx, ast) {
289 const scope = ctx.scopes[ctx.scopes.length - 1];
290
291 if (ast.name.type != "VARIABLE") return error(ctx, ast, "Invalid component name");
292 if (getScope(ctx, ast.name.name)) return error(ctx, ast, "Name already exists: "+ast.name.name);
293
294 scope[ast.name.name] = {
295 type: "COMPONENT"
296 };
297
298 return "";
299}
300
301function genDeclareSignal(ctx, ast) {
302 const scope = ctx.scopes[ctx.scopes.length-1];
303
304 if (ast.name.type != "VARIABLE") return error(ctx, ast, "Invalid component name");
305 if (getScope(ctx, ast.name.name)) return error(ctx, ast, "Name already exists: "+ast.name.name);
306
307 scope[ast.name.name] = {
308 type: "SIGNAL"
309 };
310
311 return "";
312}
313
314function genDeclareVariable(ctx, ast) {
315 const scope = ctx.scopes[ctx.scopes.length-1];
316
317 if (ast.name.type != "VARIABLE") return error(ctx, ast, "Invalid component name");
318 if (getScope(ctx, ast.name.name)) return error(ctx, ast, "Name already exists: "+ast.name.name);
319
320 scope[ast.name.name] = {
321 type: "VARIABLE"
322 };
323
324 return "";
325}
326
327function genNumber(ctx, ast) {
328 return `"${ast.value.toString()}"`;
329}
330
331function genVariable(ctx, ast) {
332 const v = getScope(ctx, ast.name);
333
334 const sels = [];
335 for (let i=0; i<ast.selectors.length; i++) {
336 sels.push(gen(ctx, ast.selectors[i]));
337 if (ctx.error) return;
338 }
339
340 if (!v) {
341 return error(ctx, ast, "Invalid left operand");
342 }
343 if (v.type == "VARIABLE") {
344 return `ctx.getVar("${ast.name}",[${sels.join(",")}])`;
345 } else if (v.type == "SIGNAL") {
346 return `ctx.getSignal("${ast.name}", [${sels.join(",")}])`;
347 } else {
348 error(ctx, ast, "Invalid Variable type");
349 }
350}
351
352function genPin(ctx, ast) {
353 let componentName = ast.component.name;
354 let componentSelectors = [];
355 for (let i=0; i<ast.component.selectors.length; i++) {
356 componentSelectors.push(gen(ctx, ast.component.selectors[i]));
357 }
358 componentSelectors = "["+ componentSelectors.join(",") + "]";
359 let pinName = ast.pin.name;
360 let pinSelectors = [];
361 for (let i=0; i<ast.pin.selectors.length; i++) {
362 pinSelectors.push(gen(ctx, ast.pin.selectors[i]));
363 }
364 pinSelectors = "["+ pinSelectors.join(",") + "]";
365 return `ctx.getPin("${componentName}", ${componentSelectors}, "${pinName}", ${pinSelectors})`;
366}
367
368function genVarAssignement(ctx, ast) {
369
370 const sels = [];
371 let vName;
372
373 if (ctx.error) return;
374
375 if (ast.values[0].type == "PIN") {
376 let componentName = ast.values[0].component.name;
377 let componentSelectors = [];
378 for (let i=0; i<ast.values[0].component.selectors.length; i++) {
379 componentSelectors.push(gen(ctx, ast.values[0].component.selectors[i]));
380 }
381 componentSelectors = "["+ componentSelectors.join(",") + "]";
382 let pinName = ast.values[0].pin.name;
383 let pinSelectors = [];
384 for (let i=0; i<ast.values[0].pin.selectors.length; i++) {
385 pinSelectors.push(gen(ctx, ast.values[0].pin.selectors[i]));
386 }
387 pinSelectors = "["+ pinSelectors.join(",") + "]";
388 const res = gen(ctx, ast.values[1]);
389 return `ctx.setPin("${componentName}", ${componentSelectors}, "${pinName}", ${pinSelectors}, ${res})`;
390 }
391
392 if (ast.values[0].type == "DECLARE") {
393 gen(ctx, ast.values[0]);
394 if (ctx.error) return;
395 vName = ast.values[0].name.name;
396 } else {
397 vName = ast.values[0].name;
398 for (let i=0; i<ast.values[0].selectors.length; i++) {
399 sels.push(gen(ctx, ast.values[0].selectors[i]));
400 if (ctx.error) return;
401 }
402 }
403
404
405 const v = getScope(ctx, vName);
406
407 // Component instantiation is already done.
408 if (v.type == "COMPONENT") return "";
409
410 const res = gen(ctx, ast.values[1]);
411 if (v.type == "VARIABLE") {
412 return `ctx.setVar("${vName}", [${sels.join(",")}], ${res})`;
413 } else if (v.type == "SIGNAL") {
414 return `ctx.setSignal("${vName}", [${sels.join(",")}], ${res})`;
415 } else {
416 return error(ctx, ast, "Assigning to invalid");
417 }
418}
419
420function genConstrain(ctx, ast) {
421 const a = gen(ctx, ast.values[0]);
422 if (ctx.error) return;
423 const b = gen(ctx, ast.values[1]);
424 if (ctx.error) return;
425 return `ctx.assert(${a}, ${b})`;
426}
427
428function genSignalAssignConstrain(ctx, ast) {
429 return genVarAssignement(ctx, ast) + ";\n" + genConstrain(ctx, ast);
430}
431
432function genVarAddAssignement(ctx, ast) {
433 return genVarAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "+", values: ast.values}]});
434}
435
436function genVarMulAssignement(ctx, ast) {
437 return genVarAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "*", values: ast.values}]});
438}
439
440function genPlusPlusRight(ctx, ast) {
441 return `(${genVarAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "+", values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}]}]})}).add(__P__).sub(bigInt(1)).mod(__P__)`;
442}
443
444function genPlusPlusLeft(ctx, ast) {
445 return genVarAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "+", values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}]}]});
446}
447
448function genMinusMinusRight(ctx, ast) {
449 return `(${genVarAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "-", values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}]}]})}).add(__P__).sub(bigInt(1)).mod(__P__)`;
450}
451
452function genMinusMinusLeft(ctx, ast) {
453 return genVarAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "-", values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}]}]});
454}
455
456function genAdd(ctx, ast) {
457 const a = gen(ctx, ast.values[0]);
458 if (ctx.error) return;
459 const b = gen(ctx, ast.values[1]);
460 if (ctx.error) return;
461 return `bigInt(${a}).add(bigInt(${b})).mod(__P__)`;
462}
463
464function genMul(ctx, ast) {
465 const a = gen(ctx, ast.values[0]);
466 if (ctx.error) return;
467 const b = gen(ctx, ast.values[1]);
468 if (ctx.error) return;
469 return `bigInt(${a}).mul(bigInt(${b})).mod(__P__)`;
470}
471
472function genSub(ctx, ast) {
473 const a = gen(ctx, ast.values[0]);
474 if (ctx.error) return;
475 const b = gen(ctx, ast.values[1]);
476 if (ctx.error) return;
477 return `bigInt(${a}).add(__P__).sub(bigInt(${b})).mod(__P__)`;
478}
479
480function genDiv(ctx, ast) {
481 const a = gen(ctx, ast.values[0]);
482 if (ctx.error) return;
483 const b = gen(ctx, ast.values[1]);
484 if (ctx.error) return;
485
486 return `bigInt(${a}).mul( bigInt(${b}).inverse(__P__) ).mod(__P__)`;
487}
488
489function genIDiv(ctx, ast) {
490 const a = gen(ctx, ast.values[0]);
491 if (ctx.error) return;
492 const b = gen(ctx, ast.values[1]);
493 if (ctx.error) return;
494
495 return `bigInt(${a}).div( bigInt(${b}))`;
496}
497
498function genExp(ctx, ast) {
499 const a = gen(ctx, ast.values[0]);
500 if (ctx.error) return;
501 const b = gen(ctx, ast.values[1]);
502 if (ctx.error) return;
503 return `bigInt(${a}).modPow(bigInt(${b}), __P__)`;
504}
505
506function genBAnd(ctx, ast) {
507 const a = gen(ctx, ast.values[0]);
508 if (ctx.error) return;
509 const b = gen(ctx, ast.values[1]);
510 if (ctx.error) return;
511 return `bigInt(${a}).and(bigInt(${b})).and(__MASK__)`;
512}
513
514function genAnd(ctx, ast) {
515 const a = gen(ctx, ast.values[0]);
516 if (ctx.error) return;
517 const b = gen(ctx, ast.values[1]);
518 if (ctx.error) return;
519 return `((bigInt(${a}).neq(bigInt(0)) && bigInt(${b}).neq(bigInt(0))) ? bigInt(1) : bigInt(0))`;
520}
521
522function genOr(ctx, ast) {
523 const a = gen(ctx, ast.values[0]);
524 if (ctx.error) return;
525 const b = gen(ctx, ast.values[1]);
526 if (ctx.error) return;
527 return `((bigInt(${a}).neq(bigInt(0)) || bigInt(${b}).neq(bigInt(0))) ? bigInt(1) : bigInt(0))`;
528}
529
530function genShl(ctx, ast) {
531 const a = gen(ctx, ast.values[0]);
532 if (ctx.error) return;
533 const b = gen(ctx, ast.values[1]);
534 if (ctx.error) return;
535 return `bigInt(${b}).greater(bigInt(256)) ? 0 : bigInt(${a}).shl(bigInt(${b})).and(__MASK__)`;
536}
537
538function genShr(ctx, ast) {
539 const a = gen(ctx, ast.values[0]);
540 if (ctx.error) return;
541 const b = gen(ctx, ast.values[1]);
542 if (ctx.error) return;
543 return `bigInt(${b}).greater(bigInt(256)) ? 0 : bigInt(${a}).shr(bigInt(${b})).and(__MASK__)`;
544}
545
546function genMod(ctx, ast) {
547 const a = gen(ctx, ast.values[0]);
548 if (ctx.error) return;
549 const b = gen(ctx, ast.values[1]);
550 if (ctx.error) return;
551 return `bigInt(${a}).mod(bigInt(${b}))`;
552}
553
554function genLt(ctx, ast) {
555 const a = gen(ctx, ast.values[0]);
556 if (ctx.error) return;
557 const b = gen(ctx, ast.values[1]);
558 if (ctx.error) return;
559 return `bigInt(${a}).lt(bigInt(${b})) ? 1 : 0`;
560}
561
562function genGt(ctx, ast) {
563 const a = gen(ctx, ast.values[0]);
564 if (ctx.error) return;
565 const b = gen(ctx, ast.values[1]);
566 if (ctx.error) return;
567 return `bigInt(${a}).gt(bigInt(${b})) ? 1 : 0`;
568}
569
570function genLte(ctx, ast) {
571 const a = gen(ctx, ast.values[0]);
572 if (ctx.error) return;
573 const b = gen(ctx, ast.values[1]);
574 if (ctx.error) return;
575 return `bigInt(${a}).lesserOrEquals(bigInt(${b})) ? 1 : 0`;
576}
577
578function genGte(ctx, ast) {
579 const a = gen(ctx, ast.values[0]);
580 if (ctx.error) return;
581 const b = gen(ctx, ast.values[1]);
582 if (ctx.error) return;
583 return `bigInt(${a}).greaterOrEquals(bigInt(${b})) ? 1 : 0`;
584}
585
586function genEq(ctx, ast) {
587 const a = gen(ctx, ast.values[0]);
588 if (ctx.error) return;
589 const b = gen(ctx, ast.values[1]);
590 if (ctx.error) return;
591 return `(bigInt(${a}).eq(bigInt(${b})) ? 1 : 0)`;
592}
593
594function genNeq(ctx, ast) {
595 const a = gen(ctx, ast.values[0]);
596 if (ctx.error) return;
597 const b = gen(ctx, ast.values[1]);
598 if (ctx.error) return;
599 return `(bigInt(${a}).eq(bigInt(${b})) ? 0 : 1)`;
600}
601
602function genUMinus(ctx, ast) {
603 const a = gen(ctx, ast.values[0]);
604 if (ctx.error) return;
605 return `__P__.sub(bigInt(${a})).mod(__P__)`;
606}
607
608function genTerCon(ctx, ast) {
609 const a = gen(ctx, ast.values[0]);
610 if (ctx.error) return;
611 const b = gen(ctx, ast.values[1]);
612 if (ctx.error) return;
613 const c = gen(ctx, ast.values[2]);
614 if (ctx.error) return;
615 return `bigInt(${a}).neq(bigInt(0)) ? (${b}) : (${c})`;
616}
617
618function genInclude(ctx, ast) {
619 return ast.block ? gen(ctx, ast.block) : "";
620}
621
622function genArray(ctx, ast) {
623 let S = "[";
624 for (let i=0; i<ast.values.length; i++) {
625 if (i>0) S += ",";
626 S += gen(ctx, ast.values[i]);
627 }
628 S+="]";
629 return S;
630}
631