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