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.codeHeader += `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
541 let constCIdx = null;
542 if (cIdxRef>=0) {
543 const cIdx = ctx.refs[cIdxRef];
544 if (utils.isDefined(cIdx.value)) {
545 constCIdx = cIdx.value.toString()+"_"+label;
546 }
547 } else {
548 constCIdx = "_cIdx_"+label;
549 }
550 if (constCIdx && ctx.getSignalOffsetCache[constCIdx]) return ctx.getSignalOffsetCache[constCIdx];
551
552 const refOffset = newRef(ctx, "INT", "_sigIdx");
553 const offset = ctx.refs[refOffset];
554 instantiateRef(ctx, refOffset);
555
556 const h = utils.fnvHash(label);
557 let s;
558 if (cIdxRef>=0) {
559 const cIdx = ctx.refs[cIdxRef];
560 s = cIdx.label;
561 } else {
562 s = "__cIdx";
563 }
564 if (constCIdx) {
565 ctx.codeHeader += `${offset.label} = ctx->getSignalOffset(${s}, 0x${h}LL /* ${label} */);\n`;
566 ctx.getSignalOffsetCache[constCIdx] = refOffset;
567 } else {
568 ctx.code += `${offset.label} = ctx->getSignalOffset(${s}, 0x${h}LL /* ${label} */);\n`;
569 }
570 return refOffset;
571}
572
573function genGetSignalSizes(ctx, cIdxRef, label) {
574 let constCIdx = null;
575 if (cIdxRef>=0) {
576 const cIdx = ctx.refs[cIdxRef];
577 if (utils.isDefined(cIdx.value)) {
578 constCIdx = cIdx.value.toString()+"_"+label;
579 }
580 } else {
581 constCIdx = "_cIdx_"+label;
582 }
583 if (constCIdx && ctx.getSignalSizesCache[constCIdx]) return ctx.getSignalSizesCache[constCIdx];
584
585
586 const refSizes = newRef(ctx, "SIZES", "_sigSizes");
587 const sizes = ctx.refs[refSizes];
588 instantiateRef(ctx, refSizes);
589
590 let s;
591 if (cIdxRef>=0) {
592 const cIdx = ctx.refs[cIdxRef];
593 s = cIdx.label;
594 } else {
595 s = "__cIdx";
596 }
597 const h = utils.fnvHash(label);
598 if (constCIdx) {
599 ctx.codeHeader += `${sizes.label} = ctx->getSignalSizes(${s}, 0x${h}LL /* ${label} */);\n`;
600 ctx.getSignalSizesCache[constCIdx] = refSizes;
601 } else {
602 ctx.code += `${sizes.label} = ctx->getSignalSizes(${s}, 0x${h}LL /* ${label} */);\n`;
603 }
604 return refSizes;
605}
606
607function genSetSignal(ctx, cIdxRef, sIdxRef, valueRef) {
608 const v = ctx.refs[valueRef];
609 if (!utils.isDefined(v)) {
610 console.log("BREAK!!!");
611 }
612 if (!v.used) {
613 instantiateRef(ctx, valueRef, v.value);
614 }
615 let s;
616 if (cIdxRef>=0) {
617 const cIdx = ctx.refs[cIdxRef];
618 s = cIdx.label;
619 } else {
620 s = "__cIdx";
621 }
622 const sIdx = ctx.refs[sIdxRef];
623 ctx.code += `ctx->setSignal(__cIdx, ${s}, ${sIdx.label}, ${v.label});\n`;
624
625 return valueRef;
626}
627
628function genGetSignal(ctx, cIdxRef, sIdxRef) {
629 const resRef = newRef(ctx, "BIGINT", "_sigValue");
630 const res = ctx.refs[resRef];
631 instantiateRef(ctx, resRef);
632 let s;
633 if (cIdxRef>=0) {
634 const cIdx = ctx.refs[cIdxRef];
635 s = cIdx.label;
636 } else {
637 s = "__cIdx";
638 }
639 const sIdx = ctx.refs[sIdxRef];
640 ctx.code += `ctx->getSignal(__cIdx, ${s}, ${sIdx.label}, ${res.label});\n`;
641 return resRef;
642}
643
644function genPinAssignement(ctx, ast) {
645 let vcIdx;
646 if (ast.values[0].component.selectors.length>0) {
647 const vcOffset = genGetSubComponentOffset(ctx, -1, ast.values[0].component.name);
648 const vcSizes = genGetSubComponentSizes(ctx, -1, ast.values[0].component.name);
649 vcIdx = genGetOffset(ctx, vcOffset, vcSizes, ast.values[0].component.selectors );
650 } else {
651 vcIdx = genGetSubComponentOffset(ctx, -1, ast.values[0].component.name);
652 }
653
654 let vsIdx;
655 if (ast.values[0].pin.selectors.length>0) {
656 const vsOffset = genGetSigalOffset(ctx, vcIdx, ast.values[0].pin.name);
657 const vsSizes = genGetSignalSizes(ctx, vcIdx, ast.values[0].pin.name);
658 vsIdx = genGetOffset(ctx, vsOffset, vsSizes, ast.values[0].pin.selectors );
659 } else {
660 vsIdx = genGetSigalOffset(ctx, vcIdx, ast.values[0].pin.name);
661 }
662
663 const vVal = gen(ctx, ast.values[1]);
664
665 genSetSignal(ctx, vcIdx, vsIdx, vVal);
666
667 return vVal;
668}
669
670function genSignalAssignmen(ctx, ast, lName, sels, rName) {
671 let vsIdx;
672 const signal = ctx.refs[lName];
673 if (sels.length>0) {
674 const vsOffset = genGetSigalOffset(ctx, -1, signal.label);
675 const vsSizes = genGetSignalSizes(ctx, -1, signal.label);
676 vsIdx = genGetOffset(ctx, vsOffset, vsSizes, sels );
677 } else {
678 vsIdx = genGetSigalOffset(ctx, -1, signal.label);
679 }
680
681 return genSetSignal(ctx, -1, vsIdx, rName);
682}
683
684function genVarAssignment(ctx, ast, lRef, sels, rRef) {
685
686 const left = ctx.refs[lRef];
687 const right = ctx.refs[rRef];
688 if (!utils.isDefined(left.sizes)) {
689 left.sizes = right.sizes;
690 left.value = new Array(left.sizes[0]);
691 }
692 if (!utils.sameSizes(left.sizes.slice(sels.length), right.sizes)) return ctx.throwError(ast, "Sizes do not match");
693
694 const oRef = genGetOffset(ctx, 0, left.sizes, sels);
695 const offset = ctx.refs[oRef];
696
697 let instantiated=false;
698 if (left.used) {
699 instantiateRef(ctx, rRef, right.value);
700 instantiated=true;
701 } else if (right.used) {
702 if (sels.length == 0) {
703 instantiateRef(ctx,lRef);
704 } else {
705 instantiateRef(ctx,lRef, left.value);
706 }
707 instantiated=true;
708 } else if (offset.used) {
709 instantiateRef(ctx, rRef, right.value);
710 if (sels.length == 0) {
711 instantiateRef(ctx,lRef);
712 } else {
713 instantiateRef(ctx,lRef, left.value);
714 }
715 instantiated=true;
716 }
717
718 if (instantiated) {
719 if (offset.used) {
720 ctx.code += `Fr_copyn(${left.label} + ${offset.label}, ${right.label}, ${right.sizes[0]});\n`;
721 } else {
722 if (offset.value[0]>0) {
723 ctx.code += `Fr_copyn(${left.label} + ${offset.value[0]}, ${right.label}, ${right.sizes[0]});\n`;
724 } else {
725 ctx.code += `Fr_copyn(${left.label}, ${right.label}, ${right.sizes[0]});\n`;
726 }
727 }
728 } else {
729 if (offset.value[0]>0) {
730 for (let i=0; i<right.sizes[0]; i++) left.value[offset.value[0] + i] = right.value[i];
731 } else {
732 for (let i=0; i<right.sizes[0]; i++) left.value[i] = right.value[i];
733 }
734 }
735}
736
737function genAssignement(ctx, ast) {
738
739 let lRef;
740 let sels;
741
742 if (ctx.error) return;
743
744 if (ast.values[0].type == "PIN") return genPinAssignement(ctx, ast);
745
746 if (ast.values[0].type == "DECLARE") {
747 lRef = gen(ctx, ast.values[0]);
748 if (ctx.error) return;
749 sels = [];
750 } else {
751 lRef = ast.values[0].refId;
752 sels = ast.values[0].selectors;
753 }
754
755
756 const left = ctx.refs[lRef];
757 if (!left) return ctx.throwError(ast, "Variable does not exists: "+ast.values[0].name);
758
759 // Component instantiation is already done.
760 if (left.type == "COMPONENT") return;
761
762 const rRef = gen(ctx, ast.values[1]);
763 if (ctx.error) return;
764
765 if (left.type == "SIGNAL") return genSignalAssignmen(ctx, ast, lRef, sels, rRef);
766
767 if (left.type == "BIGINT") return genVarAssignment(ctx, ast, lRef, sels, rRef);
768
769 return ctx.throwError(ast, "Assigning to invalid");
770}
771
772function genConstraint(ctx, ast) {
773 const aRef = gen(ctx, ast.values[0]);
774 const a = ctx.refs[aRef];
775 if (ctx.error) return;
776 const bRef = gen(ctx, ast.values[1]);
777 const b = ctx.refs[bRef];
778 if (ctx.error) return;
779 const strErr = ast.fileName + ":" + ast.first_line + ":" + ast.first_column;
780 instantiateRef(ctx, aRef, a.value);
781 instantiateRef(ctx, bRef, b.value);
782 ctx.code += `ctx->checkConstraint(__cIdx, ${a.label}, ${b.label}, "${strErr}");`;
783}
784
785
786function genArray(ctx, ast) {
787 let subSizes;
788 let instantiate = false;
789 if (ast.values.length == 0) return ctx.throwError(ast, "Arrays with zero elements not allowed");
790 const value = [];
791 const refs = [];
792 for (let i=0; i<ast.values.length; i++) {
793 const eRef = gen(ctx, ast.values[i]);
794 const e = ctx.refs[eRef];
795
796 if (i==0) {
797 subSizes = e.sizes;
798 } else {
799 if (!utils.sameSizes(subSizes, e.sizes)) return ctx.throwError(ast, "Heteroeneus array not allowed");
800 }
801 if (e.used) {
802 instantiate = true;
803 }
804 if (!instantiate) {
805 value.push(...e.value);
806 }
807 refs.push(eRef);
808 }
809
810 const newSize = [subSizes[0]*ast.values.length , ...subSizes];
811
812 if (instantiate) {
813 const rRef = newRef(ctx, "BIGINT", "_arr", null, newSize);
814 instantiateRef(ctx, rRef);
815 for (let i=0; i<ast.values.length; i++) {
816 const v = ctx.refs[refs[i]];
817 const r = ctx.refs[rRef];
818 ctx.code += `Fr_copyn(${r.label}+${i*subSizes[0]}, ${v.label}, ${subSizes[0]});\n`;
819 }
820 return rRef;
821 } else {
822 const rRef = newRef(ctx, "BIGINT", "_arr", value, newSize);
823 return rRef;
824 }
825
826}
827
828
829function genFunctionCall(ctx, ast) {
830 if (ast.name == "log") {
831 const vRef = gen(ctx, ast.params[0]);
832 const val = ctx.refs[vRef];
833 instantiateRef(ctx, vRef, val.value);
834 ctx.code+=`ctx->log(${val.label});`;
835 return vRef;
836 }
837 const params = [];
838 for (let i=0; i<ast.params.length; i++) {
839 const pRef = gen(ctx, ast.params[i]);
840 params.push(ctx.refs[pRef]);
841 }
842
843 const fn = ctx.buildFunction(ast.name, params);
844 if (ctx.error) return;
845
846 if (fn.type == "VARVAL_CONSTSIZE") {
847 const resRef = newRef(ctx, "BIGINT", `_ret${ast.name}Sizes`, null, fn.returnSizes);
848 const res = ctx.refs[resRef];
849 instantiateRef(ctx, resRef);
850
851 ctx.code +=`${fn.fnName}(ctx, ${res.label}`;
852 for (let i=0; i<params.length; i++) {
853 if (params[i].used) ctx.code+=`,${params[i].label}`;
854 }
855 ctx.code+=");\n";
856
857 return resRef;
858 } else {
859 const res = newRef(ctx, "BIGINT", "_retVal", fn.returnValue, fn.returnSizes);
860 return res;
861 }
862}
863
864function enterConditionalCode(ctx, ast) {
865 if (!ctx.conditionalCode) {
866 iterateAST(ast, (ast) => {
867 if (ast.type == "OP") {
868 if (["=", "+=", "*=", "PLUSPLUSLEFT", "PLUSPLUSRIGHT", "MINUSMINUSLEFT", "MINUSMINUSRIGHT"].indexOf(ast.op) >= 0) {
869 let refId;
870 refId = ast.values[0].refId;
871 const ref = ctx.refs[refId];
872 instantiateRef(ctx, refId, ref.value);
873 }
874 }
875 });
876 ctx.conditionalCode = 1;
877 } else {
878 ctx.conditionalCode ++;
879 }
880}
881
882function leaveConditionalCode(ctx) {
883 assert(ctx.conditionalCode, "Leaving conditional code too many times");
884 ctx.conditionalCode --;
885 if (!ctx.conditionalCode) {
886 delete ctx.conditionalCode;
887 }
888}
889
890function genLoop(ctx, ast) {
891 genLoopSrcComment(ctx, ast);
892 let inLoop = false;
893
894 if (ast.init) {
895 gen(ctx, ast.init);
896 if (ctx.error) return;
897 }
898
899 let end=false;
900 let condVarRef;
901 let condVar;
902
903
904 const condRef = gen(ctx, ast.condition);
905 if (ctx.error) return;
906
907 const cond = ctx.refs[condRef];
908 if (!utils.sameSizes(cond.sizes, [1,0])) return ctx.throwError(ast.condition, "Operation cannot be done on an array");
909
910 if (cond.used) {
911 inLoop = true;
912 enterConditionalCode(ctx, ast);
913 condVarRef = newRef(ctx, "INT", "_cond");
914 condVar = ctx.refs[condVarRef];
915 instantiateRef(ctx, condVarRef);
916
917 ctx.code +=
918 `${condVar.label} = Fr_isTrue(${cond.label});\n` +
919 `while (${condVar.label}) {\n`;
920 } else {
921 if (!utils.isDefined(cond.value)) return ctx.throwError(ast, "condition value not assigned");
922 if (cond.value[0].isZero()) end=true;
923 }
924
925
926 while (!end) {
927
928 const oldCode = ctx.code;
929 ctx.code = "";
930
931 if (ast.body.type != "BLOCK") genSrcComment(ctx, ast.body);
932 gen(ctx, ast.body);
933 if (ctx.error) return;
934
935 if (ast.step) {
936 gen(ctx, ast.step);
937 if (ctx.error) return;
938 }
939
940 const condRef2 = gen(ctx, ast.condition);
941 if (ctx.error) return;
942
943 const cond2 = ctx.refs[condRef2];
944
945 if ((!cond2.used) &&(haveCode(ctx.code))) {
946 instantiateRef(ctx, condRef2, cond2.value);
947 }
948
949 if (!inLoop) {
950 if (cond2.used) {
951 ctx.code = oldCode + ctx.code;
952 inLoop = true;
953 enterConditionalCode(ctx, ast);
954 condVarRef = newRef(ctx, "INT", "_cond");
955 condVar = ctx.refs[condVarRef];
956 instantiateRef(ctx, condVarRef);
957
958 ctx.code +=
959 `${condVar.label} = Fr_isTrue(${cond2.label});\n` +
960 `while (${condVar.label}) {\n`;
961 } else {
962 ctx.code = oldCode + ctx.code;
963 if (cond2.value[0].isZero()) end=true;
964 }
965 } else {
966 ctx.code =
967 oldCode +
968 utils.ident(
969 ctx.code +
970 `${condVar.label} = Fr_isTrue(${cond2.label});\n`);
971 end=true;
972 }
973 }
974 if (inLoop) {
975 ctx.code += "}\n";
976 leaveConditionalCode(ctx);
977 }
978 ctx.scopes.pop();
979
980 function haveCode(c) {
981 return c.replace(/\/\*[\s\S]*?\*\/|\/\/.*/g, "").trim() != "";
982 }
983}
984
985function genIf(ctx, ast) {
986 genIfSrcComment(ctx, ast);
987 const condRef = gen(ctx, ast.condition);
988 if (ctx.error) return;
989 const cond = ctx.refs[condRef];
990 if (!utils.sameSizes(cond.sizes, [1,0])) return ctx.throwError(ast.condition, "Operation cannot be done on an array");
991
992 if (cond.used) {
993 enterConditionalCode(ctx, ast);
994
995 ctx.code += `if (Fr_isTrue(${cond.label})) {\n`;
996
997 const oldCode = ctx.code;
998 ctx.code = "";
999
1000 gen(ctx, ast.then);
1001 if (ctx.error) return;
1002
1003 ctx.code = oldCode + utils.ident(ctx.code);
1004
1005 if (ast.else) {
1006 ctx.code += "} else {\n";
1007 const oldCode = ctx.code;
1008 ctx.code = "";
1009 gen(ctx, ast.else);
1010 if (ctx.error) return;
1011 ctx.code = oldCode + utils.ident(ctx.code);
1012 }
1013
1014 ctx.code += "}\n";
1015 leaveConditionalCode(ctx);
1016
1017 } else {
1018 if (!utils.isDefined(cond.value)) return ctx.throwError(ast, "condition value not assigned");
1019 if (!cond.value[0].isZero()) {
1020 gen(ctx, ast.then);
1021 } else {
1022 if (ast.else) {
1023 gen(ctx, ast.else);
1024 }
1025 }
1026 }
1027}
1028
1029
1030function genReturn(ctx, ast) {
1031 const vRef = gen(ctx, ast.value);
1032 const v= ctx.refs[vRef];
1033 if (ctx.returnSizes) {
1034 if (!utils.sameSizes(v.sizes, ctx.returnSizes)) return ctx.throwError(ast, "Diferent return sizes");
1035 } else {
1036 ctx.returnSizes = v.sizes;
1037 }
1038 if (ctx.conditionalCode) {
1039 instantiateRef(ctx, vRef, v.value);
1040 }
1041 if (v.used) {
1042 ctx.code += `Fr_copyn(__retValue, ${v.label}, ${v.sizes[0]});\n`;
1043 } else {
1044 if (!utils.isDefined(v.value)) return ctx.throwError(ast, "Returning an unknown value");
1045 if (!utils.isDefined(ctx.returnValue)) {
1046 ctx.returnValue = v.value;
1047 }
1048 }
1049 ctx.code += "goto returnFunc;\n";
1050 return vRef;
1051}
1052
1053
1054
1055function genSignalAssignConstraint(ctx, ast) {
1056 const res = genAssignement(ctx, ast);
1057 // genConstraint(ctx, ast);
1058 return res;
1059 // return genVarAssignement(ctx, ast);
1060}
1061
1062function genVarAddAssignement(ctx, ast) {
1063 return genAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "+", values: ast.values}]});
1064}
1065
1066function genVarMulAssignement(ctx, ast) {
1067 return genAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "*", values: ast.values}]});
1068}
1069
1070function genOpOp(ctx, ast, op, lr) {
1071
1072 if (ast.values[0].type != "VARIABLE") return ctx.throwError(ast, "incrementing a non variable");
1073
1074 const vRef = ast.values[0].refId;
1075
1076 const vevalRef = gen(ctx, ast.values[0]);
1077 if (ctx.error) return;
1078 const veval = ctx.refs[vevalRef];
1079
1080 if (veval.type != "BIGINT") return ctx.throwError(ast, "incrementing a non variable");
1081
1082 const resRef = newRef(ctx, "BIGINT", "_tmp");
1083 const res = ctx.refs[resRef];
1084 if (veval.used) {
1085 instantiateRef(ctx, resRef);
1086 ctx.code += `Fr_${op}(${res.label}, ${veval.label}, ctx->circuit->constants + ${ctx.addConstant(bigInt.one)});\n`;
1087 } else {
1088 res.value = [ctx.field[op](veval.value[0], bigInt(1))];
1089 }
1090 genVarAssignment(ctx, ast, vRef, ast.values[0].selectors, resRef);
1091 if (lr == "RIGHT") {
1092 return vevalRef;
1093 } else if (lr == "LEFT") {
1094 return resRef;
1095 }
1096}
1097
1098function genOp(ctx, ast, op, nOps) {
1099 const vals = [];
1100 const valRefs = [];
1101
1102 var anyUsed=false;
1103
1104 for (let i=0; i<nOps; i++) {
1105 const ref = gen(ctx, ast.values[i]);
1106 if (ctx.error) return;
1107 let v = ctx.refs[ref];
1108 valRefs.push(ref);
1109 vals.push(v);
1110
1111 if (!utils.sameSizes(v.sizes, [1,0])) return ctx.throwError(ast, "Operation cannot be done on an array");
1112 if ( (!v.used)
1113 &&( (!utils.isDefined(v.value))
1114 ||(!utils.isDefined(v.value[0]))))
1115 return ctx.throwError(ast, "Using a not assigned varialble: ");
1116
1117 if (v.used) anyUsed=true;
1118 }
1119
1120 let rRef;
1121 if (anyUsed) {
1122 for (let i=0; i<nOps; i++) {
1123 if (!vals[i].used) {
1124 valRefs[i] = instantiateConstant(ctx, vals[i].value);
1125 vals[i] = ctx.refs[valRefs[i]];
1126 }
1127 }
1128
1129 rRef = newRef(ctx, "BIGINT", "_tmp");
1130 instantiateRef(ctx, rRef);
1131 const r = ctx.refs[rRef];
1132 let c = `Fr_${op}(${r.label}`;
1133 for (let i=0; i<nOps; i++) {
1134 c+=`,${vals[i].label}`;
1135 }
1136 c += ");\n";
1137 ctx.code += c;
1138 } else {
1139 const params = [];
1140 for (let i=0; i<nOps; i++) {
1141 params.push(vals[i].value[0]);
1142 }
1143 rRef = newRef(ctx, "BIGINT", "_tmp", ctx.field[op](...params));
1144 }
1145 return rRef;
1146}
1147
1148
1149function genTerCon(ctx, ast) {
1150 const condRef = gen(ctx, ast.values[0]);
1151 if (ctx.error) return;
1152 const cond = ctx.refs[condRef];
1153 if (!utils.sameSizes(cond.sizes, [1,0])) return ctx.throwError(ast.condition, "Operation cannot be done on an array");
1154
1155 let oldCode;
1156 if (cond.used) {
1157 enterConditionalCode(ctx, ast);
1158
1159 const rLabel = ctx.getUniqueName("_ter");
1160
1161 ctx.codeHeader += `PFrElement ${rLabel};\n`;
1162 ctx.code += `if (Fr_isTrue(${cond.label})) {\n`;
1163
1164 oldCode = ctx.code;
1165 ctx.code = "";
1166
1167 const thenRef = gen(ctx, ast.values[1]);
1168 if (ctx.error) return;
1169 const then = ctx.refs[thenRef];
1170
1171 instantiateRef(ctx, thenRef, then.value);
1172
1173 ctx.code = oldCode + utils.ident(ctx.code);
1174
1175 ctx.code += `${rLabel} = ${then.label};\n`;
1176
1177 ctx.code += "} else {\n";
1178
1179 oldCode = ctx.code;
1180 ctx.code = "";
1181 const elseRef = gen(ctx, ast.values[2]);
1182 if (ctx.error) return;
1183 const els = ctx.refs[elseRef];
1184
1185 instantiateRef(ctx, elseRef, els.value);
1186
1187 ctx.code = oldCode + utils.ident(ctx.code);
1188
1189 ctx.code += `${rLabel} = ${els.label};\n`;
1190
1191 ctx.code += "}\n";
1192
1193
1194 if (!utils.sameSizes(then.sizes, els.sizes)) return ctx.throwError(ast, "Ternary op must return the same sizes");
1195
1196 const refId = ctx.refs.length;
1197 ctx.refs.push({
1198 type: "BIGINT",
1199 sizes: then.sizes,
1200 used: true,
1201 label: rLabel
1202 });
1203
1204 return refId;
1205
1206 } else {
1207 if (!utils.isDefined(cond.value)) return ctx.throwError(ast, "condition value not assigned");
1208 if (!cond.value[0].isZero()) {
1209 return gen(ctx, ast.values[1]);
1210 } else {
1211 return gen(ctx, ast.values[2]);
1212 }
1213 }
1214}
1215
1216function genInclude(ctx, ast) {
1217 return ast.block ? gen(ctx, ast.block) : "";
1218}
1219
1220