circom/src/c_gen.js
src/c_gen.jsRaw
1const bigInt = require("big-integer");
2const utils = require("./utils");
3const assert = require("assert");
4const iterateAST = require("./iterateast");
5
6module.exports.gen = gen;
7module.exports.newRef = newRef;
8module.exports.createRefs = createRefs;
9
10function newRef(ctx, type, name, value, sizes) {
11 let label;
12 if (!name) {
13 label = ctx.getUniqueName();
14 } else {
15 label = ctx.getUniqueName(name);
16 }
17 if (Array.isArray(sizes)) {
18 // sizes = sizes;
19 } else if (utils.isDefined(value)) {
20 sizes = utils.accSizes(utils.extractSizes(value));
21 } else {
22 sizes = [1, 0];
23 }
24
25 const refId = ctx.refs.length;
26 const entry = {
27 type: type,
28 used: false,
29 sizes: sizes,
30 label: label
31 };
32 if (utils.isDefined(value)) {
33 entry.value = utils.flatArray(value);
34 }
35
36 ctx.refs.push(entry);
37 return refId;
38}
39
40function instantiateRef(ctx, refId, initValue) {
41 const v = ctx.refs[refId];
42 if (v.used) return;
43 if (!v.sizes) v.sizes = [1,0];
44
45 if (v.type=="BIGINT") {
46 ctx.codeHeader += `FrElement ${v.label}[${v.sizes[0]}];\n`;
47 } else if (v.type=="INT") {
48 ctx.codeHeader += `int ${v.label};\n`;
49 } else if (v.type=="SIZES") {
50 ctx.codeHeader += `Circom_Sizes ${v.label};\n`;
51 }
52 v.used = true;
53 if (utils.isDefined(initValue)) {
54 if (v.type == "BIGINT") {
55 for (let i=0; i<initValue.length; i++) {
56 if (utils.isDefined(initValue[i])) {
57 const idConstant = ctx.addConstant(initValue[i]);
58 ctx.code += `Fr_copy(&(${v.label}[${i}]), ctx->circuit->constants +${idConstant});\n`;
59 }
60 }
61 }
62 }
63}
64
65function instantiateConstant(ctx, value) {
66 const sizes = utils.accSizes(utils.extractSizes(value));
67 const flatedValue = utils.flatArray(value);
68 const label = ctx.getUniqueName("_const");
69 ctx.codeHeader += `FrElement ${label}[${sizes[0]}];\n`;
70 for (let i=0; i<flatedValue.length; i++) {
71 const idConstant = ctx.addConstant(flatedValue[i]);
72 ctx.codeHeader += `Fr_copy(&(${label}[${i}]), ctx->circuit->constants +${idConstant});\n`;
73 }
74 const refId = ctx.refs.length;
75 ctx.refs.push({
76 type: "BIGINT",
77 used: true,
78 sizes: sizes,
79 label: label
80 });
81 return refId;
82}
83
84
85function createRefs(ctx, ast) {
86 const scopeLabels = [];
87 iterateAST(ast, (ast, level) => {
88 while ((scopeLabels.length>0)&&(!level.startsWith(scopeLabels[scopeLabels.length-1]))) {
89 ctx.scopes.pop();
90 scopeLabels.pop();
91 }
92 if (ast.type == "DECLARE") {
93 if (ctx.scopes[ctx.scopes.length-1][ast.name.name]) return ctx.throwError(ast, `Name already defined: ${ast.name.name}`);
94 for (let i=ctx.scopes.length-2; i>=0; i--) {
95 if (ctx.scopes[i][ast.name.name]) ctx.logWarning(ast, `Shadowing variable: ${ast.name.name}`);
96 }
97 let entry = {};
98 if (ast.declareType == "COMPONENT") {
99 entry.type= "COMPONENT";
100 entry.label = ast.name.name;
101 } else if ((ast.declareType == "SIGNALIN")||
102 (ast.declareType == "SIGNALOUT")||
103 (ast.declareType == "SIGNAL")) {
104 entry.type = "SIGNAL";
105 entry.label = ast.name.name;
106 } else if (ast.declareType == "VARIABLE") {
107 entry.type = "BIGINT";
108 entry.used = false;
109 entry.sizes = null;
110 entry.label = ctx.getUniqueName(ast.name.name);
111 } else {
112 return ctx.throwError(ast, "Invalid declaration: " + ast.declareType);
113 }
114
115 const refId = ctx.refs.length;
116 ctx.refs.push(entry);
117 ast.refId = refId;
118 ctx.scopes[ctx.scopes.length-1][ast.name.name] = refId;
119 } else if (["BLOCK", "FOR", "IF", "WHILE", "COMPUTE"].indexOf(ast.type) >= 0) {
120 scopeLabels.push(level);
121 ctx.scopes.push({});
122 } else if (ast.type == "VARIABLE") {
123 ast.refId = name2ref(ast.name);
124 }
125 });
126
127 function name2ref(n) {
128 for (let i=ctx.scopes.length-1; i>=0; i--) {
129 if (typeof ctx.scopes[i][n] !== "undefined") return ctx.scopes[i][n];
130 }
131 return -1;
132 }
133}
134
135function gen(ctx, ast) {
136 if ((ast.type == "NUMBER") ) {
137 return genNumber(ctx, ast);
138 } else if (ast.type == "VARIABLE") {
139 return genVariable(ctx, ast);
140 } else if (ast.type == "PIN") {
141 return genPin(ctx, ast);
142 } else if (ast.type == "OP") {
143 if (ast.op == "=") {
144 return genAssignement(ctx, ast);
145 } else if (ast.op == "<--") {
146 return genAssignement(ctx, ast);
147 } else if (ast.op == "<==") {
148 return genSignalAssignConstraint(ctx, ast);
149 } else if (ast.op == "===") {
150 return genConstraint(ctx, ast);
151 } else if (ast.op == "+=") {
152 return genVarAddAssignement(ctx, ast);
153 } else if (ast.op == "*=") {
154 return genVarMulAssignement(ctx, ast);
155 } else if (ast.op == "+") {
156 return genOp(ctx, ast, "add", 2);
157 } else if (ast.op == "-") {
158 return genOp(ctx, ast, "sub", 2);
159 } else if (ast.op == "UMINUS") {
160 return genOp(ctx, ast, "neg", 1);
161 } else if (ast.op == "*") {
162 return genOp(ctx, ast, "mul", 2);
163 } else if (ast.op == "%") {
164 return genOp(ctx, ast, "mod", 2);
165 } else if (ast.op == "PLUSPLUSRIGHT") {
166 return genOpOp(ctx, ast, "add", "RIGHT");
167 } else if (ast.op == "PLUSPLUSLEFT") {
168 return genOpOp(ctx, ast, "add", "LEFT");
169 } else if (ast.op == "MINUSMINUSRIGHT") {
170 return genOpOp(ctx, ast, "sub", "RIGHT");
171 } else if (ast.op == "MINUSMINUSLEFT") {
172 return genOpOp(ctx, ast, "sub", "LEFT");
173 } else if (ast.op == "**") {
174 return genOp(ctx, ast, "pow", 2);
175 } else if (ast.op == "/") {
176 return genOp(ctx, ast, "div", 2);
177 } else if (ast.op == "\\") {
178 return genOp(ctx, ast, "idiv", 2);
179 } else if (ast.op == "&") {
180 return genOp(ctx, ast, "band", 2);
181 } else if (ast.op == "|") {
182 return genOp(ctx, ast, "bor", 2);
183 } else if (ast.op == "^") {
184 return genOp(ctx, ast, "bxor", 2);
185 } else if (ast.op == "~") {
186 return genOp(ctx, ast, "bnot", 1);
187 } else if (ast.op == "&&") {
188 return genOp(ctx, ast, "land", 2);
189 } else if (ast.op == "||") {
190 return genOp(ctx, ast, "lor", 2);
191 } else if (ast.op == "!") {
192 return genOp(ctx, ast, "lnot", 1);
193 } else if (ast.op == "<<") {
194 return genOp(ctx, ast, "shl", 2);
195 } else if (ast.op == ">>") {
196 return genOp(ctx, ast, "shr", 2);
197 } else if (ast.op == "<") {
198 return genOp(ctx, ast, "lt", 2);
199 } else if (ast.op == ">") {
200 return genOp(ctx, ast, "gt", 2);
201 } else if (ast.op == "<=") {
202 return genOp(ctx, ast, "leq", 2);
203 } else if (ast.op == ">=") {
204 return genOp(ctx, ast, "geq", 2);
205 } else if (ast.op == "==") {
206 return genOp(ctx, ast, "eq", 2);
207 } else if (ast.op == "!=") {
208 return genOp(ctx, ast, "neq", 2);
209 } else if (ast.op == "?") {
210 return genTerCon(ctx, ast);
211 } else {
212 ctx.throwError(ast, "Invalid operation: " + ast.op);
213 }
214 } else if (ast.type == "DECLARE") {
215 if (ast.declareType == "COMPONENT") {
216 return genDeclareComponent(ctx, ast);
217 } else if ((ast.declareType == "SIGNALIN")||
218 (ast.declareType == "SIGNALOUT")||
219 (ast.declareType == "SIGNAL")) {
220 return genDeclareSignal(ctx, ast);
221 } else if (ast.declareType == "VARIABLE") {
222 return genDeclareVariable(ctx, ast);
223 } else {
224 ctx.throwError(ast, "Invalid declaration: " + ast.declareType);
225 }
226 } else if (ast.type == "FUNCTIONCALL") {
227 return genFunctionCall(ctx, ast);
228 } else if (ast.type == "BLOCK") {
229 return genBlock(ctx, ast);
230 } else if (ast.type == "COMPUTE") {
231 return gen(ctx, ast.body);
232 } else if (ast.type == "FOR") {
233 return genLoop(ctx, ast);
234 } else if (ast.type == "WHILE") {
235 return genLoop(ctx, ast);
236 } else if (ast.type == "IF") {
237 return genIf(ctx, ast);
238 } else if (ast.type == "RETURN") {
239 return genReturn(ctx, ast);
240 } else if (ast.type == "INCLUDE") {
241 return genInclude(ctx, ast);
242 } else if (ast.type == "ARRAY") {
243 return genArray(ctx, ast);
244 } else {
245 ctx.throwError(ast, "GEN -> Invalid AST node type: " + ast.type);
246 }
247}
248
249function genBlock(ctx, ast) {
250 ctx.scopes.push({});
251 const oldCode = ctx.code;
252 let res = null;
253 ctx.code = "";
254 for (let i=0; i<ast.statements.length; i++) {
255 if (["BLOCK", "COMPUTE", "FOR", "WHILE", "IF"].indexOf(ast.statements[i].type)<0) {
256 genSrcComment(ctx, ast.statements[i]);
257 }
258 res = gen(ctx, ast.statements[i]);
259 if (ctx.error) return;
260 }
261 ctx.code = oldCode + ctx.code;
262 ctx.scopes.pop();
263 return res;
264}
265
266function getSource(ctx, ast) {
267 let codes = [];
268 const fl= ast.first_line-1;
269 const ll = ast.last_line-1;
270 const fc = ast.first_column;
271 const lc = ast.last_column;
272 for (let i=fl; i<=ll; i++) {
273 const p1 = i==fl ? fc : 0;
274 const p2 = i==ll ? lc : -1;
275 codes.push(ctx.includedFiles[ctx.fileName][i].slice(p1, p2>=0 ? p2 : undefined));
276 }
277 return codes.join("\n");
278}
279
280function genSrcComment(ctx, ast) {
281 const code = getSource(ctx, ast);
282 ctx.code += "\n/* "+code+" */\n";
283}
284
285function genLoopSrcComment(ctx, ast) {
286 if (ast.type == "FOR") {
287 const init = getSource(ctx, ast.init);
288 const condition = getSource(ctx, ast.condition);
289 const step = getSource(ctx, ast.step);
290 ctx.code += `\n/* for (${init},${condition},${step}) */\n`;
291 } else if (ast.type == "WHILE") {
292 const condition = getSource(ctx, ast.condition);
293 ctx.code += `\n/* while (${condition}) */\n`;
294 } else {
295 assert(false, "Invalid loop type: "+ ast.type);
296 }
297}
298
299function genIfSrcComment(ctx, ast) {
300 const condition = getSource(ctx, ast.condition);
301 ctx.code += `\n/* if (${condition}) */\n`;
302}
303
304
305function genDeclareComponent(ctx, ast) {
306 return ast.refId;
307}
308
309function genDeclareSignal(ctx, ast) {
310 return ast.refId;
311}
312
313function genDeclareVariable(ctx, ast) {
314
315
316 const v = ctx.refs[ast.refId];
317
318 let sizes;
319 if (ast.name.selectors.length>0) {
320 sizes=[];
321 for (let i=0; i< ast.name.selectors.length; i++) {
322 const sizeRef = gen(ctx, ast.name.selectors[i]);
323 const size = ctx.refs[sizeRef];
324 if (size.sizes[0] != 1) return ctx.throwError(ast, "A selector cannot be an array");
325 if (size.used) return ctx.throwError(ast, "Variable size variables not allowed");
326 sizes.push(size.value[0].toJSNumber());
327 }
328 sizes = utils.accSizes(sizes);
329 } else {
330 sizes = [1,0];
331 }
332
333 if ((!v.sizes)&&(sizes)) {
334 v.sizes = sizes;
335 v.value = new Array(sizes[0]);
336 }
337
338 if (v.sizes) {
339 if (!utils.sameSizes(v.sizes, sizes)) return ctx.throwError(ast, "Redefined a var with different sized");
340 }
341
342 return ast.refId;
343}
344
345function genNumber(ctx, ast) {
346 return newRef(ctx, "BIGINT", "_num", bigInt(ast.value));
347}
348
349
350
351function genGetOffset(ctx, refOffset, vSizes, sels) {
352
353 let rN = 0;
354 let rStr = "";
355 let offset;
356
357 if (refOffset) {
358 offset = ctx.refs[refOffset];
359 if (offset.used) {
360 rStr += offset.label;
361 } else {
362 rN += offset.value.toJSNumber();
363 }
364 }
365
366 if ((sels)&&(sels.length>0)) {
367
368 let iSizes;
369 if (Array.isArray(vSizes)) {
370 iSizes = {
371 used: false,
372 sizes: vSizes
373 };
374 } else {
375 iSizes = ctx.refs[vSizes];
376 }
377
378 for (let i=0; i<sels.length; i++) {
379 const idxRef = gen(ctx, sels[i]);
380 const iIdx = ctx.refs[idxRef];
381
382 if ((!iIdx.used) && (!iSizes.used)) {
383 rN = rN + iIdx.value * iSizes.sizes[i+1];
384 } else {
385 if (rN>0) {
386 if (rStr != "") rStr += " + ";
387 rStr += rN;
388 rN =0;
389 }
390
391 if (rStr != "") rStr += " + ";
392 if (iIdx.used) {
393 rStr += `Fr_toInt(${iIdx.label})`;
394 } else {
395 rStr += iIdx.value[0].toString();
396 }
397 rStr += "*";
398 if (iSizes.used) {
399 rStr += `${iSizes.label}[${i+1}]`;
400 } else {
401 rStr += iSizes.sizes[i+1];
402 }
403 }
404 }
405 }
406
407 if (rStr == "") {
408 const o = newRef(ctx, "INT", "_offset", rN);
409 return o;
410 } else {
411 if (rN>0) {
412 if (rStr != "") rStr += " + ";
413 rStr += rN;
414 rN =0;
415 }
416 if ((refOffset)&&(rStr == offset.label)) {
417 return offset.label;
418 } else {
419 const resRef = newRef(ctx, "INT", "_offset");
420 instantiateRef(ctx, resRef);
421 const res = ctx.refs[resRef];
422 ctx.code += `${res.label} = ${rStr};\n`;
423 return resRef;
424 }
425 }
426}
427
428function genVariable(ctx, ast) {
429 const v = ctx.refs[ast.refId];
430
431 const l = ast.selectors ? ast.selectors.length : 0;
432
433 if (v.type == "SIGNAL") {
434 let vOffset;
435 if (l>0) {
436 const vsOffset = genGetSigalOffset(ctx, -1, ast.name);
437 const vsSizes = genGetSignalSizes(ctx, -1, ast.name);
438 vOffset = genGetOffset(ctx, vsOffset, vsSizes, ast.selectors );
439 } else {
440 vOffset = genGetSigalOffset(ctx, -1, ast.name);
441 }
442 return genGetSignal(ctx, -1, vOffset);
443
444 } else if (v.type == "BIGINT") {
445 const refOffset = genGetOffset(ctx, 0, v.sizes, ast.selectors );
446 const offset = ctx.refs[refOffset];
447 if (v.used) {
448 if (offset.used) {
449 const refRes = newRef(ctx, "BIGINT", "_v", null, v.sizes.slice(l));
450 const res = ctx.refs[refRes];
451 res.used = true;
452 ctx.codeHeader += `PFrElement ${res.label};\n`;
453 ctx.code += `${res.label} = ${v.label} + ${offset.label};\n`;
454 return refRes;
455 } else if ((offset.value[0])||(l>0)) {
456 const refRes = newRef(ctx, "BIGINT", "_v", null, v.sizes.slice(l));
457 const res = ctx.refs[refRes];
458 res.used = true;
459 ctx.codeHeader += `PFrElement ${res.label};\n`;
460 ctx.code += `${res.label} = ${v.label} + ${offset.value[0]};\n`;
461 return refRes;
462 } else {
463 return ast.refId;
464 }
465 } else {
466 if (offset.used) {
467 instantiateRef(ctx, ast.refId, v.value);
468 const resRef = newRef(ctx, "BIGINT", "_v", null, v.sizes.slice(l));
469 const res = ctx.refs[resRef];
470 res.used = true;
471 ctx.codeHeader += `PFrElement ${res.label};\n`;
472 ctx.code += `${res.label} = ${v.label} + ${offset.label};\n`;
473 return resRef;
474 } else {
475 // return newSubRef(ctx, ast.name, ast.selectors);
476 return newRef(ctx, "BIGINT", "_v", v.value.slice(offset.value[0], offset.value[0] + v.sizes[l]),v.sizes.slice(l));
477 }
478 }
479 }
480}
481
482
483function genPin(ctx, ast) {
484 let vcIdx;
485 if (ast.component.selectors.length>0) {
486 const vcOffset = genGetSubComponentOffset(ctx, -1, ast.component.name);
487 const vcSizes = genGetSubComponentSizes(ctx, -1, ast.component.name);
488 vcIdx = genGetOffset(ctx, vcOffset, vcSizes, ast.component.selectors );
489 } else {
490 vcIdx = genGetSubComponentOffset(ctx, -1, ast.component.name);
491 }
492
493 let vsIdx;
494 if (ast.pin.selectors.length>0) {
495 const vsOffset = genGetSigalOffset(ctx, vcIdx, ast.pin.name);
496 const vsSizes = genGetSignalSizes(ctx, vcIdx, ast.pin.name);
497 vsIdx = genGetOffset(ctx, vsOffset, vsSizes, ast.pin.selectors );
498 } else {
499 vsIdx = genGetSigalOffset(ctx, vcIdx, ast.pin.name);
500 }
501
502 return genGetSignal(ctx, vcIdx, vsIdx);
503}
504
505function genGetSubComponentOffset(ctx, cIdxRef, label) {
506 const refOffset = newRef(ctx, "INT", "_compIdx");
507 const offset = ctx.refs[refOffset];
508 instantiateRef(ctx, refOffset);
509
510 const h = utils.fnvHash(label);
511 let s;
512 if (cIdxRef>=0) {
513 const cIdx = ctx.refs[cIdxRef];
514 s = cIdx.label;
515 } else {
516 s = "__cIdx";
517 }
518 ctx.code += `${offset.label} = ctx->getSubComponentOffset(${s}, 0x${h}LL /* ${label} */);\n`;
519 return refOffset;
520}
521
522function genGetSubComponentSizes(ctx, cIdxRef, label) {
523 const sizesRef = newRef(ctx, "SIZES", "_compSizes");
524 const sizes = ctx.refs[sizesRef];
525 instantiateRef(ctx, sizesRef);
526
527 const h = utils.fnvHash(label);
528 let s;
529 if (cIdxRef>=0) {
530 const cIdx = ctx.refs[cIdxRef];
531 s = cIdx.label;
532 } else {
533 s = "__cIdx";
534 }
535 ctx.code += `${sizes.label} = ctx->getSubComponentSizes(${s}, 0x${h}LL /* ${label} */);\n`;
536 return sizesRef;
537}
538
539function genGetSigalOffset(ctx, cIdxRef, label) {
540 const refOffset = newRef(ctx, "INT", "_sigIdx");
541 const offset = ctx.refs[refOffset];
542 instantiateRef(ctx, refOffset);
543
544 const h = utils.fnvHash(label);
545 let s;
546 if (cIdxRef>=0) {
547 const cIdx = ctx.refs[cIdxRef];
548 s = cIdx.label;
549 } else {
550 s = "__cIdx";
551 }
552 ctx.code += `${offset.label} = ctx->getSignalOffset(${s}, 0x${h}LL /* ${label} */);\n`;
553 return refOffset;
554}
555
556function genGetSignalSizes(ctx, cIdxRef, label) {
557 const sizesRef = newRef(ctx, "SIZES", "_sigSizes");
558 const sizes = ctx.refs[sizesRef];
559 instantiateRef(ctx, sizesRef);
560
561 let s;
562 if (cIdxRef>=0) {
563 const cIdx = ctx.refs[cIdxRef];
564 s = cIdx.label;
565 } else {
566 s = "__cIdx";
567 }
568 const h = utils.fnvHash(label);
569 ctx.code += `${sizes.label} = ctx->getSignalSizes(${s}, 0x${h}LL /* ${label} */);\n`;
570 return sizesRef;
571}
572
573function genSetSignal(ctx, cIdxRef, sIdxRef, valueRef) {
574 const v = ctx.refs[valueRef];
575 if (!utils.isDefined(v)) {
576 console.log("BREAK!!!");
577 }
578 if (!v.used) {
579 instantiateRef(ctx, valueRef, v.value);
580 }
581 let s;
582 if (cIdxRef>=0) {
583 const cIdx = ctx.refs[cIdxRef];
584 s = cIdx.label;
585 } else {
586 s = "__cIdx";
587 }
588 const sIdx = ctx.refs[sIdxRef];
589 ctx.code += `ctx->setSignal(__cIdx, ${s}, ${sIdx.label}, ${v.label});\n`;
590
591 return valueRef;
592}
593
594function genGetSignal(ctx, cIdxRef, sIdxRef) {
595 const resRef = newRef(ctx, "BIGINT", "_sigValue");
596 const res = ctx.refs[resRef];
597 instantiateRef(ctx, resRef);
598 let s;
599 if (cIdxRef>=0) {
600 const cIdx = ctx.refs[cIdxRef];
601 s = cIdx.label;
602 } else {
603 s = "__cIdx";
604 }
605 const sIdx = ctx.refs[sIdxRef];
606 ctx.code += `ctx->getSignal(__cIdx, ${s}, ${sIdx.label}, ${res.label});\n`;
607 return resRef;
608}
609
610function genPinAssignement(ctx, ast) {
611 let vcIdx;
612 if (ast.values[0].component.selectors.length>0) {
613 const vcOffset = genGetSubComponentOffset(ctx, -1, ast.values[0].component.name);
614 const vcSizes = genGetSubComponentSizes(ctx, -1, ast.values[0].component.name);
615 vcIdx = genGetOffset(ctx, vcOffset, vcSizes, ast.values[0].component.selectors );
616 } else {
617 vcIdx = genGetSubComponentOffset(ctx, -1, ast.values[0].component.name);
618 }
619
620 let vsIdx;
621 if (ast.values[0].pin.selectors.length>0) {
622 const vsOffset = genGetSigalOffset(ctx, vcIdx, ast.values[0].pin.name);
623 const vsSizes = genGetSignalSizes(ctx, vcIdx, ast.values[0].pin.name);
624 vsIdx = genGetOffset(ctx, vsOffset, vsSizes, ast.values[0].pin.selectors );
625 } else {
626 vsIdx = genGetSigalOffset(ctx, vcIdx, ast.values[0].pin.name);
627 }
628
629 const vVal = gen(ctx, ast.values[1]);
630
631 genSetSignal(ctx, vcIdx, vsIdx, vVal);
632
633 return vVal;
634}
635
636function genSignalAssignmen(ctx, ast, lName, sels, rName) {
637 let vsIdx;
638 const signal = ctx.refs[lName];
639 if (sels.length>0) {
640 const vsOffset = genGetSigalOffset(ctx, -1, signal.label);
641 const vsSizes = genGetSignalSizes(ctx, -1, signal.label);
642 vsIdx = genGetOffset(ctx, vsOffset, vsSizes, sels );
643 } else {
644 vsIdx = genGetSigalOffset(ctx, -1, signal.label);
645 }
646
647 return genSetSignal(ctx, -1, vsIdx, rName);
648}
649
650function genVarAssignment(ctx, ast, lRef, sels, rRef) {
651
652 const left = ctx.refs[lRef];
653 const right = ctx.refs[rRef];
654 if (!utils.isDefined(left.sizes)) {
655 left.sizes = right.sizes;
656 left.value = new Array(left.sizes[0]);
657 }
658 if (!utils.sameSizes(left.sizes.slice(sels.length), right.sizes)) return ctx.throwError(ast, "Sizes do not match");
659
660 const oRef = genGetOffset(ctx, 0, left.sizes, sels);
661 const offset = ctx.refs[oRef];
662
663 let instantiated=false;
664 if (left.used) {
665 instantiateRef(ctx, rRef, right.value);
666 instantiated=true;
667 } else if (right.used) {
668 if (sels.length == 0) {
669 instantiateRef(ctx,lRef);
670 } else {
671 instantiateRef(ctx,lRef, left.value);
672 }
673 instantiated=true;
674 } else if (offset.used) {
675 instantiateRef(ctx, rRef, right.value);
676 if (sels.length == 0) {
677 instantiateRef(ctx,lRef);
678 } else {
679 instantiateRef(ctx,lRef, left.value);
680 }
681 instantiated=true;
682 }
683
684 if (instantiated) {
685 if (offset.used) {
686 ctx.code += `Fr_copyn(${left.label} + ${offset.label}, ${right.label}, ${right.sizes[0]});\n`;
687 } else {
688 if (offset.value[0]>0) {
689 ctx.code += `Fr_copyn(${left.label} + ${offset.value[0]}, ${right.label}, ${right.sizes[0]});\n`;
690 } else {
691 ctx.code += `Fr_copyn(${left.label}, ${right.label}, ${right.sizes[0]});\n`;
692 }
693 }
694 } else {
695 if (offset.value[0]>0) {
696 for (let i=0; i<right.sizes[0]; i++) left.value[offset.value[0] + i] = right.value[i];
697 } else {
698 for (let i=0; i<right.sizes[0]; i++) left.value[i] = right.value[i];
699 }
700 }
701}
702
703function genAssignement(ctx, ast) {
704
705 let lRef;
706 let sels;
707
708 if (ctx.error) return;
709
710 if (ast.values[0].type == "PIN") return genPinAssignement(ctx, ast);
711
712 if (ast.values[0].type == "DECLARE") {
713 lRef = gen(ctx, ast.values[0]);
714 if (ctx.error) return;
715 sels = [];
716 } else {
717 lRef = ast.values[0].refId;
718 sels = ast.values[0].selectors;
719 }
720
721
722 const left = ctx.refs[lRef];
723 if (!left) return ctx.throwError(ast, "Variable does not exists: "+ast.values[0].name);
724
725 // Component instantiation is already done.
726 if (left.type == "COMPONENT") return;
727
728 const rRef = gen(ctx, ast.values[1]);
729 if (ctx.error) return;
730
731 if (left.type == "SIGNAL") return genSignalAssignmen(ctx, ast, lRef, sels, rRef);
732
733 if (left.type == "BIGINT") return genVarAssignment(ctx, ast, lRef, sels, rRef);
734
735 return ctx.throwError(ast, "Assigning to invalid");
736}
737
738function genConstraint(ctx, ast) {
739 const aRef = gen(ctx, ast.values[0]);
740 const a = ctx.refs[aRef];
741 if (ctx.error) return;
742 const bRef = gen(ctx, ast.values[1]);
743 const b = ctx.refs[bRef];
744 if (ctx.error) return;
745 const strErr = ast.fileName + ":" + ast.first_line + ":" + ast.first_column;
746 instantiateRef(ctx, aRef, a.value);
747 instantiateRef(ctx, bRef, b.value);
748 ctx.code += `ctx->checkConstraint(__cIdx, ${a.label}, ${b.label}, "${strErr}");`;
749}
750
751
752function genArray(ctx, ast) {
753 let subSizes;
754 let instantiate = false;
755 if (ast.values.length == 0) return ctx.throwError(ast, "Arrays with zero elements not allowed");
756 const value = [];
757 const refs = [];
758 for (let i=0; i<ast.values.length; i++) {
759 const eRef = gen(ctx, ast.values[i]);
760 const e = ctx.refs[eRef];
761
762 if (i==0) {
763 subSizes = e.sizes;
764 } else {
765 if (!utils.sameSizes(subSizes, e.sizes)) return ctx.throwError(ast, "Heteroeneus array not allowed");
766 }
767 if (e.used) {
768 instantiate = true;
769 }
770 if (!instantiate) {
771 value.push(...e.value);
772 }
773 refs.push(eRef);
774 }
775
776 const newSize = [subSizes[0]*ast.values.length , ...subSizes];
777
778 if (instantiate) {
779 const rRef = newRef(ctx, "BIGINT", "_arr", null, newSize);
780 instantiateRef(ctx, rRef);
781 for (let i=0; i<ast.values.length; i++) {
782 const v = ctx.refs[refs[i]];
783 const r = ctx.refs[rRef];
784 ctx.code += `Fr_copyn(${r.label}+${i*subSizes[0]}, ${v.label}, ${subSizes[0]});\n`;
785 }
786 return rRef;
787 } else {
788 const rRef = newRef(ctx, "BIGINT", "_arr", value, newSize);
789 return rRef;
790 }
791
792}
793
794
795function genFunctionCall(ctx, ast) {
796 if (ast.name == "log") {
797 const vRef = gen(ctx, ast.params[0]);
798 const val = ctx.refs[vRef];
799 instantiateRef(ctx, vRef, val.value);
800 ctx.code+=`ctx->log(${val.label});`;
801 return vRef;
802 }
803 const params = [];
804 for (let i=0; i<ast.params.length; i++) {
805 const pRef = gen(ctx, ast.params[i]);
806 params.push(ctx.refs[pRef]);
807 }
808
809 const fn = ctx.buildFunction(ast.name, params);
810 if (ctx.error) return;
811
812 if (fn.type == "VARVAL_CONSTSIZE") {
813 const resRef = newRef(ctx, "BIGINT", `_ret${ast.name}Sizes`, null, fn.returnSizes);
814 const res = ctx.refs[resRef];
815 instantiateRef(ctx, resRef);
816
817 ctx.code +=`${fn.fnName}(ctx, ${res.label}`;
818 for (let i=0; i<params.length; i++) {
819 if (params[i].used) ctx.code+=`,${params[i].label}`;
820 }
821 ctx.code+=");\n";
822
823 return resRef;
824 } else {
825 const res = newRef(ctx, "BIGINT", "_retVal", fn.returnValue, fn.returnSizes);
826 return res;
827 }
828}
829
830function enterConditionalCode(ctx, ast) {
831 if (!ctx.conditionalCode) {
832 iterateAST(ast, (ast) => {
833 if (ast.type == "OP") {
834 if (["=", "+=", "*=", "PLUSPLUSLEFT", "PLUSPLUSRIGHT", "MINUSMINUSLEFT", "MINUSMINUSRIGHT"].indexOf(ast.op) >= 0) {
835 let refId;
836 refId = ast.values[0].refId;
837 const ref = ctx.refs[refId];
838 instantiateRef(ctx, refId, ref.value);
839 }
840 }
841 });
842 ctx.conditionalCode = 1;
843 } else {
844 ctx.conditionalCode ++;
845 }
846}
847
848function leaveConditionalCode(ctx) {
849 assert(ctx.conditionalCode, "Leaving conditional code too many times");
850 ctx.conditionalCode --;
851 if (!ctx.conditionalCode) {
852 delete ctx.conditionalCode;
853 }
854}
855
856function genLoop(ctx, ast) {
857 genLoopSrcComment(ctx, ast);
858 let inLoop = false;
859
860 if (ast.init) {
861 gen(ctx, ast.init);
862 if (ctx.error) return;
863 }
864
865 let end=false;
866 let condVarRef;
867 let condVar;
868
869
870 const condRef = gen(ctx, ast.condition);
871 if (ctx.error) return;
872
873 const cond = ctx.refs[condRef];
874 if (!utils.sameSizes(cond.sizes, [1,0])) return ctx.throwError(ast.condition, "Operation cannot be done on an array");
875
876 if (cond.used) {
877 inLoop = true;
878 enterConditionalCode(ctx, ast);
879 condVarRef = newRef(ctx, "INT", "_cond");
880 condVar = ctx.refs[condVarRef];
881 instantiateRef(ctx, condVarRef);
882
883 ctx.code +=
884 `${condVar.label} = Fr_isTrue(${cond.label});\n` +
885 `while (${condVar.label}) {\n`;
886 } else {
887 if (!utils.isDefined(cond.value)) return ctx.throwError(ast, "condition value not assigned");
888 if (cond.value[0].isZero()) end=true;
889 }
890
891
892 while (!end) {
893
894 const oldCode = ctx.code;
895 ctx.code = "";
896
897 if (ast.body.type != "BLOCK") genSrcComment(ctx, ast.body);
898 gen(ctx, ast.body);
899 if (ctx.error) return;
900
901 if (ast.step) {
902 gen(ctx, ast.step);
903 if (ctx.error) return;
904 }
905
906 const condRef2 = gen(ctx, ast.condition);
907 if (ctx.error) return;
908
909 const cond2 = ctx.refs[condRef2];
910
911 if ((!cond2.used) &&(haveCode(ctx.code))) {
912 instantiateRef(ctx, condRef2, cond2.value);
913 }
914
915 if (!inLoop) {
916 if (cond2.used) {
917 ctx.code = oldCode + ctx.code;
918 inLoop = true;
919 enterConditionalCode(ctx, ast);
920 condVarRef = newRef(ctx, "INT", "_cond");
921 condVar = ctx.refs[condVarRef];
922 instantiateRef(ctx, condVarRef);
923
924 ctx.code +=
925 `${condVar.label} = Fr_isTrue(${cond2.label});\n` +
926 `while (${condVar.label}) {\n`;
927 } else {
928 ctx.code = oldCode + ctx.code;
929 if (cond2.value[0].isZero()) end=true;
930 }
931 } else {
932 ctx.code =
933 oldCode +
934 utils.ident(
935 ctx.code +
936 `${condVar.label} = Fr_isTrue(${cond2.label});\n`);
937 end=true;
938 }
939 }
940 if (inLoop) {
941 ctx.code += "}\n";
942 leaveConditionalCode(ctx);
943 }
944 ctx.scopes.pop();
945
946 function haveCode(c) {
947 return c.replace(/\/\*[\s\S]*?\*\/|\/\/.*/g, "").trim() != "";
948 }
949}
950
951function genIf(ctx, ast) {
952 genIfSrcComment(ctx, ast);
953 const condRef = gen(ctx, ast.condition);
954 if (ctx.error) return;
955 const cond = ctx.refs[condRef];
956 if (!utils.sameSizes(cond.sizes, [1,0])) return ctx.throwError(ast.condition, "Operation cannot be done on an array");
957
958 if (cond.used) {
959 enterConditionalCode(ctx, ast);
960
961 ctx.code += `if (Fr_isTrue(${cond.label})) {\n`;
962
963 const oldCode = ctx.code;
964 ctx.code = "";
965
966 gen(ctx, ast.then);
967 if (ctx.error) return;
968
969 ctx.code = oldCode + utils.ident(ctx.code);
970
971 if (ast.else) {
972 ctx.code += "} else {\n";
973 const oldCode = ctx.code;
974 ctx.code = "";
975 gen(ctx, ast.else);
976 if (ctx.error) return;
977 ctx.code = oldCode + utils.ident(ctx.code);
978 }
979
980 ctx.code += "}\n";
981 leaveConditionalCode(ctx);
982
983 } else {
984 if (!utils.isDefined(cond.value)) return ctx.throwError(ast, "condition value not assigned");
985 if (!cond.value[0].isZero()) {
986 gen(ctx, ast.then);
987 } else {
988 if (ast.else) {
989 gen(ctx, ast.else);
990 }
991 }
992 }
993}
994
995
996function genReturn(ctx, ast) {
997 const vRef = gen(ctx, ast.value);
998 const v= ctx.refs[vRef];
999 if (ctx.returnSizes) {
1000 if (!utils.sameSizes(v.sizes, ctx.returnSizes)) return ctx.throwError(ast, "Diferent return sizes");
1001 } else {
1002 ctx.returnSizes = v.sizes;
1003 }
1004 if (ctx.conditionalCode) {
1005 instantiateRef(ctx, vRef, v.value);
1006 }
1007 if (v.used) {
1008 ctx.code += `Fr_copyn(__retValue, ${v.label}, ${v.sizes[0]});\n`;
1009 } else {
1010 if (!utils.isDefined(v.value)) return ctx.throwError(ast, "Returning an unknown value");
1011 if (!utils.isDefined(ctx.returnValue)) {
1012 ctx.returnValue = v.value;
1013 }
1014 }
1015 ctx.code += "goto returnFunc;\n";
1016 return vRef;
1017}
1018
1019
1020
1021function genSignalAssignConstraint(ctx, ast) {
1022 const res = genAssignement(ctx, ast);
1023 // genConstraint(ctx, ast);
1024 return res;
1025 // return genVarAssignement(ctx, ast);
1026}
1027
1028function genVarAddAssignement(ctx, ast) {
1029 return genAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "+", values: ast.values}]});
1030}
1031
1032function genVarMulAssignement(ctx, ast) {
1033 return genAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "*", values: ast.values}]});
1034}
1035
1036function genOpOp(ctx, ast, op, lr) {
1037
1038 if (ast.values[0].type != "VARIABLE") return ctx.throwError(ast, "incrementing a non variable");
1039
1040 const vRef = ast.values[0].refId;
1041
1042 const vevalRef = gen(ctx, ast.values[0]);
1043 if (ctx.error) return;
1044 const veval = ctx.refs[vevalRef];
1045
1046 if (veval.type != "BIGINT") return ctx.throwError(ast, "incrementing a non variable");
1047
1048 const resRef = newRef(ctx, "BIGINT", "_tmp");
1049 const res = ctx.refs[resRef];
1050 if (veval.used) {
1051 instantiateRef(ctx, resRef);
1052 ctx.code += `Fr_${op}(${res.label}, ${veval.label}, ctx->circuit->constants + ${ctx.addConstant(bigInt.one)});\n`;
1053 } else {
1054 res.value = [ctx.field[op](veval.value[0], bigInt(1))];
1055 }
1056 genVarAssignment(ctx, ast, vRef, ast.values[0].selectors, resRef);
1057 if (lr == "RIGHT") {
1058 return vevalRef;
1059 } else if (lr == "LEFT") {
1060 return resRef;
1061 }
1062}
1063
1064function genOp(ctx, ast, op, nOps) {
1065 const vals = [];
1066 const valRefs = [];
1067
1068 var anyUsed=false;
1069
1070 for (let i=0; i<nOps; i++) {
1071 const ref = gen(ctx, ast.values[i]);
1072 if (ctx.error) return;
1073 let v = ctx.refs[ref];
1074 valRefs.push(ref);
1075 vals.push(v);
1076
1077 if (!utils.sameSizes(v.sizes, [1,0])) return ctx.throwError(ast, "Operation cannot be done on an array");
1078 if ( (!v.used)
1079 &&( (!utils.isDefined(v.value))
1080 ||(!utils.isDefined(v.value[0]))))
1081 return ctx.throwError(ast, "Using a not assigned varialble: ");
1082
1083 if (v.used) anyUsed=true;
1084 }
1085
1086 let rRef;
1087 if (anyUsed) {
1088 for (let i=0; i<nOps; i++) {
1089 if (!vals[i].used) {
1090 valRefs[i] = instantiateConstant(ctx, vals[i].value);
1091 vals[i] = ctx.refs[valRefs[i]];
1092 }
1093 }
1094
1095 rRef = newRef(ctx, "BIGINT", "_tmp");
1096 instantiateRef(ctx, rRef);
1097 const r = ctx.refs[rRef];
1098 let c = `Fr_${op}(${r.label}`;
1099 for (let i=0; i<nOps; i++) {
1100 c+=`,${vals[i].label}`;
1101 }
1102 c += ");\n";
1103 ctx.code += c;
1104 } else {
1105 const params = [];
1106 for (let i=0; i<nOps; i++) {
1107 params.push(vals[i].value[0]);
1108 }
1109 rRef = newRef(ctx, "BIGINT", "_tmp", ctx.field[op](...params));
1110 }
1111 return rRef;
1112}
1113
1114
1115function genTerCon(ctx, ast) {
1116 const condRef = gen(ctx, ast.values[0]);
1117 if (ctx.error) return;
1118 const cond = ctx.refs[condRef];
1119 if (!utils.sameSizes(cond.sizes, [1,0])) return ctx.throwError(ast.condition, "Operation cannot be done on an array");
1120
1121 let oldCode;
1122 if (cond.used) {
1123 enterConditionalCode(ctx, ast);
1124
1125 const rLabel = ctx.getUniqueName("_ter");
1126
1127 ctx.codeHeader += `PFrElement ${rLabel};\n`;
1128 ctx.code += `if (Fr_isTrue(${cond.label})) {\n`;
1129
1130 oldCode = ctx.code;
1131 ctx.code = "";
1132
1133 const thenRef = gen(ctx, ast.values[1]);
1134 if (ctx.error) return;
1135 const then = ctx.refs[thenRef];
1136
1137 instantiateRef(ctx, thenRef, then.value);
1138
1139 ctx.code = oldCode + utils.ident(ctx.code);
1140
1141 ctx.code += `${rLabel} = ${then.label};\n`;
1142
1143 ctx.code += "} else {\n";
1144
1145 oldCode = ctx.code;
1146 ctx.code = "";
1147 const elseRef = gen(ctx, ast.values[2]);
1148 if (ctx.error) return;
1149 const els = ctx.refs[elseRef];
1150
1151 instantiateRef(ctx, elseRef, els.value);
1152
1153 ctx.code = oldCode + utils.ident(ctx.code);
1154
1155 ctx.code += `${rLabel} = ${els.label};\n`;
1156
1157 ctx.code += "}\n";
1158
1159
1160 if (!utils.sameSizes(then.sizes, els.sizes)) return ctx.throwError(ast, "Ternary op must return the same sizes");
1161
1162 const refId = ctx.refs.length;
1163 ctx.refs.push({
1164 type: "BIGINT",
1165 sizes: then.sizes,
1166 used: true,
1167 label: rLabel
1168 });
1169
1170 return refId;
1171
1172 } else {
1173 if (!utils.isDefined(cond.value)) return ctx.throwError(ast, "condition value not assigned");
1174 if (!cond.value[0].isZero()) {
1175 return gen(ctx, ast.values[1]);
1176 } else {
1177 return gen(ctx, ast.values[2]);
1178 }
1179 }
1180}
1181
1182function genInclude(ctx, ast) {
1183 return ast.block ? gen(ctx, ast.block) : "";
1184}
1185
1186