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