circom/src/construction_phase.js
src/construction_phase.jsRaw
1/*
2 Copyright 2018 0KIMS association.
3
4 This file is part of circom (Zero Knowledge Circuit Compiler).
5
6 circom is a free software: you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 circom is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
14 License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with circom. If not, see <https://www.gnu.org/licenses/>.
18*/
19
20/*
21
22 The exec functions, return an object of the form:
23
24 {
25 t: "C" or "V" or "S"
26 s: Accumulated Sizes
27 v: [] only when "V" Array of values where each value is:
28 {
29 t: "N", "S", "LC", "QEX", "NQ"
30 v: When "N" its the bigInt of the number
31 coefs: { sIdx: vCoef } when "LC" the values of the coefs
32 a,b,c: a LC object of a quadratic expression.
33 }
34 refId: In case of a variable, the reference of a variable.
35 sIdx: When signal, the sIdx
36 cIdx: When component, the cIdx
37 }
38
39
40
41
42
43 */
44
45const path = require("path");
46const fs = require("fs");
47const assert = require("assert");
48
49const iterateAST = require("./iterateast");
50const utils = require("./utils");
51
52const bigInt = require("big-integer");
53
54const LCAlgebra = require("./lcalgebra");
55const parser = require("../parser/jaz.js").parser;
56
57/* TODO: Add lines information
58
59function setLines(dst, first, last) {
60 last = last || first;
61 dst.first_line = first.first_line;
62 dst.first_column = first.first_column;
63 dst.last_line = last.last_line;
64 dst.last_column = last.last_column;
65}
66*/
67
68module.exports = constructionPhase;
69
70const NQVAL = {t: "V", s:[1,0], v: [{t:"NQ"}]};
71
72function constructionPhase(ctx, srcFile) {
73
74 const fullFileName = srcFile;
75 const fullFilePath = path.dirname(fullFileName);
76
77 const src = fs.readFileSync(fullFileName, "utf8");
78 ctx.ast = parser.parse(src);
79
80 assert(ctx.ast.type == "BLOCK");
81
82 ctx.lc = new LCAlgebra(ctx.field);
83 ctx.filePath= fullFilePath;
84 ctx.fileName= fullFileName;
85 ctx.includedFiles = {};
86 ctx.includedFiles[fullFileName] = src.split("\n");
87
88 ctx.refs = [];
89 createRefs(ctx, ctx.ast);
90 exec(ctx, ctx.ast);
91
92}
93
94function exec(ctx, ast) {
95 if (!ast) {
96 return ctx.throwError(ast, "Null AST");
97 }
98 if (ast.type == "NUMBER") {
99 return execNumber(ctx,ast);
100 } else if (ast.type == "VARIABLE") {
101 return execVariable(ctx, ast);
102 } else if (ast.type == "PIN") {
103 return execPin(ctx, ast);
104 } else if (ast.type == "OP") {
105 if (ast.op == "=") {
106 return execAssignement(ctx, ast);
107 } else if (ast.op == "<--") {
108 return execAssignement(ctx, ast);
109 } else if (ast.op == "<==") {
110 return execSignalAssignConstrain(ctx, ast);
111 } else if (ast.op == "===") {
112 return execConstrain(ctx, ast);
113 } else if (ast.op == "+=") {
114 return execOpOp(ctx, ast, "add", "LEFT");
115 } else if (ast.op == "*=") {
116 return execOpOp(ctx, ast, "mul", "LEFT");
117 } else if (ast.op == "+") {
118 return execOp(ctx, ast, "add", 2);
119 } else if (ast.op == "-") {
120 return execOp(ctx, ast, "sub", 2);
121 } else if (ast.op == "UMINUS") {
122 return execOp(ctx, ast, "neg", 1);
123 } else if (ast.op == "*") {
124 return execOp(ctx, ast, "mul", 2);
125 } else if (ast.op == "%") {
126 return execOp(ctx, ast, "mod", 2);
127 } else if (ast.op == "PLUSPLUSRIGHT") {
128 return execOpOp(ctx, ast, "add", "RIGHT");
129 } else if (ast.op == "PLUSPLUSLEFT") {
130 return execOpOp(ctx, ast, "add", "LEFT");
131 } else if (ast.op == "MINUSMINUSRIGHT") {
132 return execOpOp(ctx, ast, "sub", "RIGHT");
133 } else if (ast.op == "MINUSMINUSLEFT") {
134 return execOpOp(ctx, ast, "sub", "LEFT");
135 } else if (ast.op == "/") {
136 return execOp(ctx, ast, "div", 2);
137 } else if (ast.op == "\\") {
138 return execOp(ctx, ast, "idiv", 2);
139 } else if (ast.op == "**") {
140 return execOp(ctx, ast, "pow", 2);
141 } else if (ast.op == "&") {
142 return execOp(ctx, ast, "band", 2);
143 } else if (ast.op == "|") {
144 return execOp(ctx, ast, "bor", 2);
145 } else if (ast.op == "^") {
146 return execOp(ctx, ast, "bxor", 2);
147 } else if (ast.op == "~") {
148 return execOp(ctx, ast, "bnot", 1);
149 } else if (ast.op == "&&") {
150 return execOp(ctx, ast, "land", 2);
151 } else if (ast.op == "||") {
152 return execOp(ctx, ast, "lor", 2);
153 } else if (ast.op == "!") {
154 return execOp(ctx, ast, "lnot", 1);
155 } else if (ast.op == "<<") {
156 return execOp(ctx, ast, "shl", 2);
157 } else if (ast.op == ">>") {
158 return execOp(ctx, ast, "shr", 2);
159 } else if (ast.op == "<") {
160 return execOp(ctx, ast, "lt", 2);
161 } else if (ast.op == ">") {
162 return execOp(ctx, ast, "gt", 2);
163 } else if (ast.op == "<=") {
164 return execOp(ctx, ast, "leq", 2);
165 } else if (ast.op == ">=") {
166 return execOp(ctx, ast, "geq", 2);
167 } else if (ast.op == "==") {
168 return execOp(ctx, ast, "eq", 2);
169 } else if (ast.op == "!=") {
170 return execOp(ctx, ast, "neq", 2);
171 } else if (ast.op == "?") {
172 return execTerCon(ctx, ast);
173 } else {
174 ctx.throwError(ast, "Invalid operation: " + ast.op);
175 }
176 } else if (ast.type == "DECLARE") {
177 if (ast.declareType == "COMPONENT") {
178 return execDeclareComponent(ctx, ast);
179 } else if ((ast.declareType == "SIGNALIN")||
180 (ast.declareType == "SIGNALOUT")||
181 (ast.declareType == "SIGNAL")) {
182 return execDeclareSignal(ctx, ast);
183 } else if (ast.declareType == "VARIABLE") {
184 return execDeclareVariable(ctx, ast);
185 } else {
186 ctx.throwError(ast, "Invalid declaration: " + ast.declareType);
187 }
188 } else if (ast.type == "FUNCTIONCALL") {
189 return execFunctionCall(ctx, ast);
190 } else if (ast.type == "BLOCK") {
191 return execBlock(ctx, ast);
192 } else if (ast.type == "COMPUTE") {
193 return execCompute(ctx, ast);
194 } else if (ast.type == "FOR") {
195 return execLoop(ctx, ast);
196 } else if (ast.type == "WHILE") {
197 return execLoop(ctx, ast);
198 } else if (ast.type == "IF") {
199 return execIf(ctx, ast);
200 } else if (ast.type == "RETURN") {
201 return execReturn(ctx, ast);
202 } else if (ast.type == "TEMPLATEDEF") {
203 return execTemplateDef(ctx, ast);
204 } else if (ast.type == "FUNCTIONDEF") {
205 return execFunctionDef(ctx, ast);
206 } else if (ast.type == "INCLUDE") {
207 return execInclude(ctx, ast);
208 } else if (ast.type == "ARRAY") {
209 return execArray(ctx, ast);
210 } else {
211 ctx.throwError(ast, "Invalid AST node type: " + ast.type);
212 }
213}
214
215function execNumber(ctx, ast) {
216 return {
217 t: "V",
218 s:[1,0],
219 v: [{
220 t: "N",
221 v: bigInt(ast.value)
222 }]
223 };
224}
225
226
227function execTemplateDef(ctx, ast) {
228 ctx.templates[ast.name] = {
229 block: ast.block,
230 params: ast.params,
231 fileName: ctx.fileName,
232 filePath: ctx.filePath,
233 };
234}
235
236function execFunctionDef(ctx, ast) {
237 ctx.functions[ast.name] = {
238 block: ast.block,
239 params: ast.params,
240 fileName: ctx.fileName,
241 filePath: ctx.filePath
242 };
243}
244
245function execDeclareComponent(ctx, ast) {
246 if (ast.name.type != "VARIABLE") return ctx.throwError( ast, "Invalid component name");
247
248 const sizes=[];
249 for (let i=0; i< ast.name.selectors.length; i++) {
250 const sizeRef = exec(ctx, ast.name.selectors[i]);
251 if (ctx.error) return;
252
253 const size = val(ctx, sizeRef);
254 if (size.t != "N") return ctx.throwError( ast.name.selectors[i], "expected a number");
255
256 sizes.push( size.v.toJSNumber() );
257 }
258
259 let cIdx = ctx.addComponent(ast.name.name, sizes);
260 if (!Array.isArray(cIdx)) cIdx = [cIdx, cIdx+1];
261
262
263 ctx.refs[ast.refId].s = utils.accSizes(sizes);
264 ctx.refs[ast.refId].cIdx = cIdx[0];
265
266 return ctx.refs[ast.refId];
267}
268
269
270function execDeclareSignal(ctx, ast) {
271
272 if (ast.name.type != "VARIABLE") return ctx.throwError(ast, "Invalid component name");
273
274 const sizes=[];
275 for (let i=0; i< ast.name.selectors.length; i++) {
276 const size = exec(ctx, ast.name.selectors[i]);
277 if (ctx.error) return;
278 if (size.s[0] != 1) return ctx.throwError(ast, "Size cannot be an array");
279 if (size.v[0].t != "N") return ctx.throwError(ast, "Size must be declared in construction time");
280 sizes.push( size.v[0].v.toJSNumber() );
281 }
282
283 let sIdx = ctx.addSignal(ast.name.name, sizes);
284 if (!Array.isArray(sIdx)) sIdx = [sIdx, sIdx+1];
285 for (let i=sIdx[0]; i<sIdx[1]; i++) {
286 ctx.signals[i] = {
287 o: 0,
288 e: -1
289 };
290
291 if (ast.declareType == "SIGNALIN") {
292 ctx.signals[i].o |= ctx.IN;
293 ctx.components[ctx.currentComponent].nInSignals+=1;
294 }
295 if (ast.declareType == "SIGNALOUT") {
296 ctx.signals[i].o |= ctx.OUT;
297 }
298 if (ast.private ) {
299 ctx.signals[i].o |= ctx.PRV;
300 }
301 if (ctx.main) {
302 ctx.signals[i].o |= ctx.MAIN;
303 }
304
305 // ctx.components[ctx.currentComponent].signals.push(i);
306 }
307
308 const v = ctx.refs[ast.refId];
309 v.s = utils.accSizes(sizes);
310 v.sIdx = sIdx[0];
311
312 return v;
313}
314
315function execDeclareVariable(ctx, ast) {
316
317 if (ast.name.type != "VARIABLE") return ctx.throwError(ast, "Invalid linear combination name");
318
319 const sizes=[];
320 for (let i=0; i< ast.name.selectors.length; i++) {
321 const size = exec(ctx, ast.name.selectors[i]);
322 if (ctx.error) return;
323 if (size.s[0] != 1) return ctx.throwError(ast, "Size cannot be an array");
324 if (size.v[0].t != "N") return ctx.throwError(ast, "Size must be declared in construction time");
325 sizes.push( size.v[0].v.toJSNumber() );
326 }
327
328 const v = ctx.refs[ast.refId];
329
330 v.s = utils.accSizes(sizes);
331 v.v = new Array(v.s[0]);
332
333 return v;
334}
335
336function execAssignement(ctx, ast) {
337 let left;
338 const leftSels=[];
339 if (ast.values[0].type == "DECLARE") {
340 left = exec(ctx, ast.values[0]);
341 if (ctx.error) return;
342 } else if (ast.values[0].type == "PIN") {
343 left = execPin(ctx, ast.values[0]);
344 if (ctx.error) return;
345 } else {
346 if (!utils.isDefined(ast.values[0].refId)) return ctx.throwError(ast, "Assigning to a non variable");
347 left = ctx.refs[ast.values[0].refId];
348 if (ast.values[0].selectors) {
349 for (let i=0; i< ast.values[0].selectors.length; i++) {
350 const sel = exec(ctx, ast.values[0].selectors[i]);
351 if (ctx.error) return;
352
353 if (sel.s[0] != 1) return ctx.throwError(ast, "Selector cannot be an array");
354 if (sel.v[0].t != "N") return {t: "NQ"};
355
356 leftSels.push( sel.v[0].v.toJSNumber() );
357 }
358 }
359
360 }
361
362 if ((!left)||(!left.s)) return ctx.throwError(ast, "variable. not defined yet");
363
364 if (left.t == "C") return execInstantiateComponet(ctx, left, ast.values[1], leftSels);
365 if ((left.t == "S")&&( ["<--", "<==", "-->", "==>"].indexOf(ast.op) < 0)) return ctx.throwError(ast, "Cannot assign to a signal with `=` use <-- or <== ops");
366 if ((left.t == "V")&&( ["<--", "<==", "-->", "==>"].indexOf(ast.op) >= 0)) return ctx.throwError(ast, `Cannot assign to a var with ${ast.op}. use = op`);
367
368 const right = exec(ctx, ast.values[1]);
369 if (ctx.error) return;
370
371 if (!utils.sameSizes(left.s.slice(leftSels.length),right.s)) return ctx.throwError(ast, "Sizes in assignment must be the same");
372
373 let o = 0;
374 for (let i=0; i<leftSels.length; i++) o += leftSels[i]*left.s[i+1];
375 if (left.t == "V") {
376 if (right.t=="V") {
377 for (let i=0; i<right.s[0]; i++) {
378 left.v[o+i]=right.v[i];
379 }
380 } else if (right.t == "S") {
381 for (let i=0; i<right.s[0]; i++) {
382 left.v[o+i]={t: "LC", coefs: {}};
383 left.v[o+i].coefs[right.sIdx+i] = ctx.field.one;
384 }
385 }
386 } else if ( left.t == "S") {
387 if (right.t=="V") {
388 for (let i=0; i<right.s[0]; i++) {
389 const ev = ctx.lc.evaluate(ctx, right.v[i]);
390 setSignalValue(left.sIdx + o +i, ev);
391 }
392 } else if (right.t == "S") {
393 for (let i=0; i<right.s[0]; i++) {
394 joinSignals(left.sIdx + o + i, right.sIdx + i);
395 }
396 }
397 }
398
399 return right;
400
401 function setSignalValue(dSIdx, v) {
402 let sDest = ctx.signals[dSIdx];
403 while (sDest.e>=0) sDest=ctx.signals[sDest.e];
404
405 if (utils.isDefined(sDest.v)) return ctx.throwError(ast, "Signals cannot be assigned twice");
406
407 if (v !== null) {
408 sDest.v = v;
409 }
410 }
411
412 function joinSignals(dIdx, sIdx) {
413 let sDest=ctx.signals[dIdx];
414 let isOut = (sDest.o & ctx.MAIN)&&(sDest.o & ctx.OUT);
415 while (sDest.e>=0) {
416 sDest=ctx.signals[sDest.e];
417 isOut = isOut || ((sDest.o & ctx.MAIN)&&(sDest.o & ctx.OUT));
418 }
419
420 if (utils.isDefined(sDest.v)) return ctx.throwError(ast, "Signals cannot be assigned twice");
421
422 let sSrc = ctx.signals[sIdx];
423 let isIn = (sSrc.o & ctx.MAIN)&&(sSrc.o & ctx.IN);
424 while (sSrc.e>=0) {
425 sSrc=ctx.signals[sSrc.e];
426 isIn = isIn || ((sSrc.o & ctx.MAIN)&&(sSrc.o & ctx.IN));
427 }
428
429 // Skip if an out is assigned directly to an input.
430 if ((!isIn)||(!isOut)) {
431 sDest.e = sIdx;
432 } else {
433 if (utils.isDefined(sSrc.v)) sDest.v = sSrc.v;
434 }
435 }
436}
437
438
439
440function execInstantiateComponet(ctx, vr, fn, sels) {
441
442 if (fn.type != "FUNCTIONCALL") return ctx.throwError(fn, "Right type of instantiate component must be a function call");
443
444 const templateName = fn.name;
445
446 const template = ctx.templates[templateName];
447 if (!template) return ctx.throwError(fn, "Invalid Template");
448
449 const paramValues = [];
450 for (let i=0; i< fn.params.length; i++) {
451 const v = exec(ctx, fn.params[i]);
452 if (ctx.error) return;
453 for (let j=0; j<v.s[0]; j++) {
454 if (v.v[j].t != "N") ctx.throwError(fn, "Parameters of a template must be constant");
455 }
456 paramValues.push(v);
457 }
458 if (template.params.length != paramValues.length) ctx.throwError(fn, "Invalid Number of parameters");
459
460 let o=0;
461 for (let i=0; i<sels.length; i++) o += sels[i] * vr.s[i+1];
462 for (let i=o; i<o+vr.s[sels.length]; i++) {
463 instantiateComponent(vr.cIdx+i);
464 }
465
466 function instantiateComponent(cIdx) {
467
468 function extractValue(sizes, arr) {
469 if (sizes[0] == 1) return arr[0].v.toString();
470 const res = [];
471 for (let i=0; i<sizes[0]; i += sizes[1]) {
472 res.push(extractValue(sizes.slice(1), arr.slice(i, i+sizes[1])));
473 }
474 return res;
475 }
476
477 if (ctx.components[cIdx]) return ctx.throwError(fn, "Component already instantiated");
478
479 const oldComponent = ctx.currentComponent;
480 const oldFileName = ctx.fileName;
481 const oldFilePath = ctx.filePath;
482 const oldMain = ctx.main;
483 const oldRefs = ctx.refs;
484
485 if (ctx.currentComponent==-1) {
486 ctx.main=true;
487 } else {
488 ctx.main=false;
489 }
490
491 ctx.currentComponent = cIdx;
492
493 ctx.components[cIdx] = {
494 params: {},
495 names: ctx.newTableName(),
496 nInSignals: 0,
497 template: templateName
498 };
499
500
501 if (template.params.length != paramValues.length) return ctx.throwError(fn, "Invalid number of parameters: " + templateName);
502 ctx.refs = [];
503
504 const scope = {};
505 for (let i=0; i< template.params.length; i++) {
506 ctx.refs.push({
507 t: "V",
508 s: paramValues[i].s,
509 v: paramValues[i].v,
510 refId: i
511 });
512 scope[template.params[i]] = i;
513 ctx.components[cIdx].params[template.params[i]] = extractValue(paramValues[i].s, paramValues[i].v);
514 }
515
516 createRefs(ctx, template.block, scope);
517
518 ctx.fileName = template.fileName;
519 ctx.filePath = template.filePath;
520
521 execBlock(ctx, template.block);
522
523 ctx.refs = oldRefs;
524 ctx.fileName = oldFileName;
525 ctx.filePath = oldFilePath;
526 ctx.currentComponent = oldComponent;
527 ctx.main = oldMain;
528 }
529}
530
531
532function execBlock(ctx, ast) {
533 for (let i=0; i<ast.statements.length; i++) {
534 exec(ctx, ast.statements[i]);
535 if (ctx.returnValue) return;
536 if (ctx.error) return;
537 }
538}
539
540function clone(a) {
541 const res = {
542 t: a.t,
543 s: a.s
544 };
545 if (a.t == "V") {
546 res.v = new Array(a.v.length);
547 for (let i=0; i<a.v.length; i++) res.v[i] = a.v[i];
548 } else if (a.t == "S") {
549 res.sIdx = a.sIdx;
550 } else if (a.t == "C") {
551 res.cIdx = a.cIdx;
552 }
553 return res;
554}
555
556function execFunctionCall(ctx, ast) {
557
558 if (ast.name == "log") {
559 const v = exec(ctx, ast.params[0]);
560 const ev = val(ctx, v, ast);
561 if (ev.v) {
562 console.log(ev.v.toString());
563 } else {
564 console.log(JSON.stringify(ev));
565 }
566 return;
567 }
568 if (ast.name == "assert") {
569 const v = exec(ctx, ast.params[0]);
570 const ev = val(ctx, v, ast);
571 if (ev.isZero()) return ctx.throwError(ast, "Assertion failed");
572 }
573
574 const fnc = ctx.functions[ast.name];
575
576 if (!fnc) return ctx.throwError("Function not defined");
577
578 const paramValues = [];
579 for (let i=0; i< ast.params.length; i++) {
580 const v = exec(ctx, ast.params[i]);
581 if (ctx.error) return;
582
583 paramValues.push(v);
584 }
585
586 if (ast.params.length != paramValues.length) ctx.throwError(ast, "Invalid Number of parameters");
587
588 const oldFileName = ctx.fileName;
589 const oldFilePath = ctx.filePath;
590 const oldRefs = ctx.refs;
591
592 ctx.fileName = fnc.fileName;
593 ctx.filePath = fnc.filePath;
594 ctx.refs = [];
595
596 const scope = {};
597 for (let i=0; i< fnc.params.length; i++) {
598 const entry = clone(paramValues[i]);
599 entry.refId = i;
600 scope[fnc.params[i]] = i;
601 ctx.refs.push(entry);
602 }
603
604 createRefs(ctx, fnc.block, scope);
605
606 execBlock(ctx, fnc.block);
607
608 const res = ctx.returnValue;
609 ctx.returnValue = null;
610
611 ctx.fileName = oldFileName;
612 ctx.filePath = oldFilePath;
613 ctx.refs = oldRefs;
614
615 return res;
616}
617
618function execReturn(ctx, ast) {
619 ctx.returnValue = exec(ctx, ast.value);
620 return;
621}
622
623
624function execVariable(ctx, ast) {
625
626 const v = ctx.refs[ast.refId];
627 if (!v) {
628 return ctx.throwError(ast, "Variable not defined: "+ast.name);
629 }
630
631 const sels = [];
632 for (let i=0; i< ast.selectors.length; i++) {
633 const sel = exec(ctx, ast.selectors[i]);
634 if (ctx.error) return;
635 if (sel.s[0] != 1) return ctx.throwError(ast, "Variable selector cannot be an array");
636 if (sel.v[0].t != "N") return NQVAL;
637 sels.push(sel.v[0].v.toJSNumber());
638 }
639
640 let o = 0;
641 let s = v.s[0];
642
643 if (sels.length > v.s.length-2) return ctx.throwError(ast, "Too many selectors");
644 for (let i=0; i<sels.length; i++) {
645 if (sels[i] >= v.s[i] / v.s[i+1]) return ctx.throwError(ast, "Out of Range");
646 if (sels[i] < 0 ) return ctx.throwError(ast, "selector negative");
647 o += sels[i] * v.s[i+1];
648 s = v.s[i+1];
649 }
650
651 if (v.t == "V") {
652 return {
653 t: "V",
654 s: v.s.slice(sels.length),
655 v: v.v.slice(o, o+s)
656 };
657 } else if (v.t == "S") {
658 return {
659 t: "S",
660 s: v.s.slice(sels.length),
661 sIdx: o + v.sIdx
662 };
663 } else if (v.t == "C") {
664 return {
665 t: "C",
666 s: v.s.slice(sels.length),
667 cIdx: o
668 };
669 } else {
670 assert(false);
671 }
672}
673
674
675
676
677function execPin(ctx, ast) {
678 const selsC = [];
679 for (let i=0; i< ast.component.selectors.length; i++) {
680 const sel = exec(ctx, ast.component.selectors[i]);
681 if (ctx.error) return;
682
683 if (sel.s[0] != 1) return ctx.throwError(ast, "Component selector cannot be an array");
684 if (sel.v[0].t != "N") return NQVAL;
685 selsC.push(sel.v[0].v.toJSNumber());
686 }
687
688 const cIdx = ctx.getComponentIdx(ast.component.name, selsC);
689 if (cIdx<0) return ctx.throwError(ast.component, "Component does not exists: "+ast.component.name);
690
691 const selsP = [];
692 for (let i=0; i< ast.pin.selectors.length; i++) {
693 const sel = exec(ctx, ast.pin.selectors[i]);
694 if (ctx.error) return;
695 if (sel.s[0] != 1) return ctx.throwError(ast, "Signal selector cannot be an array");
696 if (sel.v[0].t != "N") return NQVAL;
697 selsP.push(sel.v[0].v.toJSNumber());
698 }
699 const sIdx = ctx.components[cIdx].names.getSignalIdx(ast.pin.name, selsP);
700
701 if (sIdx<0) return ctx.throwError(ast, "Signal not defined:" + buildFullName() );
702 return {
703 t: "S",
704 sIdx: sIdx,
705 s: utils.accSizes(ctx.components[cIdx].names.o[ast.pin.name].sizes).slice(selsP.length)
706 };
707
708 function buildFullName() {
709 return ast.component.name + sels2str(selsC) + "." + ast.pin.name + sels2str(selsP);
710 }
711
712 function sels2str(sels) {
713 let S = "";
714 for (let i=0; i< sels.length; i++) {
715 S += "[" + sels[i] + "]";
716 }
717 return S;
718 }
719}
720
721function execLoop(ctx, ast) {
722
723 if (ast.init) {
724 exec(ctx, ast.init);
725 if (ctx.error) return;
726 }
727
728 let v = exec(ctx, ast.condition);
729 if (ctx.error) return;
730 if (v.s[0] != 1) return ctx.throwError(ast.condition, "Condition in loop cannot be an array");
731 if (v.v[0].t != "N") {
732 iterateAST(ast, (ast) => {
733 if (ast.type == "OP") {
734 if (["==>", "<==", "==="].indexOf(ast.op) >= 0) {
735 return ctx.throwError(ast.condition, "Constraint inside a calculating block");
736 }
737 }
738 });
739
740 // Assert no constraints
741 return;
742 }
743
744 while ((!v.v[0].v.isZero())&&(!ctx.returnValue)) {
745 exec(ctx, ast.body);
746 if (ctx.error) return;
747
748 if (ast.step) {
749 exec(ctx, ast.step);
750 if (ctx.error) return;
751 }
752
753 v = exec(ctx, ast.condition);
754 if (ctx.error) return;
755 if (v.s[0] != 1) return ctx.throwError(ast.condition, "Condition in loop cannot be an array");
756 if (v.v[0].t != "N") return ctx.throwError(ast.condition, "Condition result not a number");
757 }
758}
759
760function execCompute(ctx, ast) {
761 iterateAST(ast, (ast) => {
762 if (ast.type == "OP") {
763 if (["==>", "<==", "==="].indexOf(ast.op) >= 0) {
764 return ctx.throwError(ast.condition, "Constraint inside a calculating block");
765 }
766 }
767 });
768}
769
770function execIf(ctx, ast) {
771 let v = exec(ctx, ast.condition);
772 if (ctx.error) return;
773 if (v.s[0] != 1) return ctx.throwError(ast.condition, "Condition cannot be an array");
774 if (v.v[0].t != "N") {
775 iterateAST(ast, (ast) => {
776 if (ast.type == "OP") {
777 if (["==>", "<==", "==="].indexOf(ast.op) >= 0) {
778 return ctx.throwError(ast.condition, "Constraint inside a calculating block");
779 }
780 }
781 });
782
783 // Assert no constraints
784 return;
785 }
786
787 if (!v.v[0].v.isZero()) {
788 exec(ctx, ast.then);
789 } else {
790 if (ast.else) {
791 exec(ctx, ast.else);
792 }
793 }
794}
795
796
797function execTerCon(ctx, ast) {
798 let v = exec(ctx, ast.values[0]);
799 if (ctx.error) return;
800 if (v.s[0] != 1) return ctx.throwError(ast.condition, "Condition cannot be an array");
801 if (v.v[0].t != "N") {
802 iterateAST(ast, (ast) => {
803 if (ast.type == "OP") {
804 if (["==>", "<==", "==="].indexOf(ast.op) >= 0) {
805 return ctx.throwError(ast.condition, "Constraint inside a calculating block");
806 }
807 }
808 });
809
810 // Assert no constraints
811 return NQVAL;
812 }
813
814 if (!v.v[0].v.isZero()) {
815 return exec(ctx, ast.values[1]);
816 } else {
817 return exec(ctx, ast.values[2]);
818 }
819}
820
821function execOp(ctx, ast, op, nOps) {
822 const operands = [];
823 for (let i=0; i<nOps; i++) {
824 const a = exec(ctx, ast.values[i]);
825 if (ctx.error) return;
826 const aV = val(ctx, a, ast.values[i]);
827 operands.push(aV);
828 }
829
830 return {
831 t: "V",
832 s: [1,0],
833 v: [ctx.lc[op](...operands)]
834 };
835}
836
837
838function execOpOp(ctx, ast, op, lr) {
839
840 if (ast.values[0].type != "VARIABLE") return ctx.throwError(ast, "incrementing a non variable");
841 if (!utils.isDefined(ast.values[0].refId)) return ctx.throwError(ast, "Assigning to a non variable");
842 const left = ctx.refs[ast.values[0].refId];
843 if (left.t != "V") return ctx.throwError(ast, `Only variables can use ${ast.op} operations`);
844 let leftSels = [];
845 if (ast.values[0].selectors) {
846 for (let i=0; i< ast.values[0].selectors.length; i++) {
847 const sel = exec(ctx, ast.values[0].selectors[i]);
848 if (ctx.error) return;
849
850 if (sel.s[0] != 1) return ctx.throwError(ast, "Selector cannot be an array");
851 if (sel.v[0].t != "N") return {t: "NQ"};
852
853 leftSels.push( sel.v[0].v.toJSNumber() );
854 }
855 }
856 if (!left.s) return ctx.throwError(ast, "variable. not defined yet");
857
858 let o = 0;
859 let s = left.s[0];
860 for (let i=0; i<leftSels.length; i++) {
861 o += leftSels[i]*left.s[i+1];
862 s = left.s[i+1];
863 }
864 if (s != 1) return ctx.throwError(ast, `invalid operator ${ast.op} in an array`);
865 const resBefore = left.v[o];
866
867 let right;
868 if (ast.values[1]) {
869 const rightRef = exec(ctx, ast.values[1]);
870 if (ctx.error) return;
871 right = val(ctx, rightRef);
872 } else {
873 right = {t:"N", v: ctx.field.one};
874 }
875
876 if (!right) return ctx.throwError(ast, "adding a no number");
877
878 const resAfter = ctx.lc[op](resBefore, right);
879 left.v[o] = resAfter;
880
881 if (lr == "RIGHT") {
882 return {
883 t: "V",
884 s: [1,0],
885 v: [resBefore]
886 };
887 } else {
888 return {
889 t: "V",
890 s: [1,0],
891 v: [resAfter]
892 };
893 }
894}
895
896// Extract the value of a reference.
897// Must be a single value. (cannot be an array)
898// The result is an LCAlgebra compatible object {t: ["N"|"LC"|"QEX"|"NQ"], [coefs|v|a,b,c]}
899function val(ctx, a, ast) {
900 if (a.s[0] != 1) return ctx.throwError(ast, "Array not allowed");
901 if (a.t=="V") {
902 return a.v[0];
903 } else if (a.t=="S") {
904 const res = {
905 t: "LC",
906 coefs: {}
907 };
908 let sIdx = a.sIdx;
909 while (ctx.signals[sIdx].e >= 0) sIdx = ctx.signals[sIdx].e;
910 res.coefs[sIdx] = ctx.field.one;
911 return res;
912 } else {
913 ctx.throwError(ast, "Invalid type: " + a.t);
914 }
915}
916
917function execConstrain(ctx, ast) {
918 ast.fileName = ctx.fileName;
919 ast.filePath = ctx.filePath;
920 const a = exec(ctx, ast.values[0]);
921 if (ctx.error) return;
922 const aV = val(ctx, a, ast.values[0]);
923 const b = exec(ctx, ast.values[1]);
924 if (ctx.error) return;
925 const bV = val(ctx, b, ast.values[1]);
926
927 const res = ctx.lc.sub(aV,bV);
928 if (res.type == "NQ") return ctx.throwError(ast, "Non Quadratic constraint");
929
930 if (!ctx.lc.isZero(res)) {
931 ctx.constraints.push(ctx.lc.toQEX(res));
932 if (ctx.verbose) {
933 if ((ctx.constraints.length % 10000 == 0)&&(ctx.constraints.length>0)) console.log("Constraints: " + ctx.constraints.length);
934 }
935 }
936
937 return a;
938}
939
940function execSignalAssignConstrain(ctx, ast) {
941 const v = execAssignement(ctx,ast);
942 if (ctx.error) return;
943 execConstrain(ctx, ast);
944 if (ctx.error) return;
945 return v;
946}
947
948function execInclude(ctx, ast) {
949 if (ast.block) {
950
951 const oldFilePath = ctx.filePath;
952 const oldFileName = ctx.fileName;
953
954 ctx.filePath = ast.filePath;
955 ctx.fileName = ast.fileName;
956
957 exec(ctx, ast.block); // Process only one.
958
959 ctx.filePath = oldFilePath;
960 ctx.fileName = oldFileName;
961 }
962}
963
964function execArray(ctx, ast) {
965 const res = {t: "V", v:[]};
966
967
968 let subSize = null;
969
970 for (let i=0; i<ast.values.length; i++) {
971 const e = exec(ctx, ast.values[i]);
972 if (i==0) {
973 subSize=e.s;
974 } else {
975 if (!utils.sameSizes(subSize, e.s)) return ctx.throwError(ast, "Array mus be homogeneous");
976 }
977 if (e.t == "V") {
978 for (let j=0; j<e.v.length;j++) res.v.push(e.v[j]);
979 } else if (e.t == "S") {
980 for (let j=0; j<e.v.length;j++) {
981 const sv = {t: "LC", coefs: {}};
982 sv.coefs[e.sIdx+j] = ctx.field.one;
983 res.v.push(sv);
984 }
985 } else {
986 return ctx.throwError(ast, "Type not allowed in array");
987 }
988 }
989
990 res.s = [ast.values.length*subSize[0], ...subSize];
991
992 return res;
993}
994
995function createRefs(ctx, ast, scope) {
996 const scopeLabels = [];
997 const scopes = scope ? [scope] : [{}];
998 iterateAST(ast, (ast, level) => {
999 while ((scopeLabels.length>0)&&(!level.startsWith(scopeLabels[scopeLabels.length-1]))) {
1000 scopes.pop();
1001 scopeLabels.pop();
1002 }
1003 if (ast.type == "DECLARE") {
1004 if (ast.declareType == "COMPONENT") {
1005 ast.refId = define(ast.name.name, {t: "C"});
1006 } else if ((ast.declareType == "SIGNALIN")||
1007 (ast.declareType == "SIGNALOUT")||
1008 (ast.declareType == "SIGNAL")) {
1009 ast.refId = define(ast.name.name, {t: "S"});
1010 } else if (ast.declareType == "VARIABLE") {
1011 ast.refId = define(ast.name.name, {t: "V", s: null});
1012 } else {
1013 return ctx.throwError(ast, "Invalid declaration: " + ast.declareType);
1014 }
1015 } else if (["BLOCK", "FOR", "IF", "WHILE", "COMPUTE"].indexOf(ast.type) >= 0) {
1016 scopeLabels.push(level);
1017 scopes.push({});
1018 } else if (ast.type == "TEMPLATEDEF") {
1019 ast.refId = define(ast.name, {t: "T"});
1020 } else if (ast.type == "FUNCTIONDEF") {
1021 ast.refId = define(ast.name, {t: "F"});
1022 } else if (ast.type == "INCLUDE") {
1023 const incFileName = path.resolve(ctx.filePath, ast.file);
1024 const incFilePath = path.dirname(incFileName);
1025
1026 ctx.includedFiles = ctx.includedFiles || [];
1027 if (ctx.includedFiles[incFileName]) {
1028 ctx.bloc = null; // Include already included...
1029 return;
1030 }
1031
1032 const src = fs.readFileSync(incFileName, "utf8");
1033
1034 if (!src) return ctx.throwError(ast, "Include file not found: "+incFileName);
1035
1036 ctx.includedFiles[incFileName] = src.split("\n");
1037
1038 ast.block = parser.parse(src);
1039 ast.filePath = incFilePath;
1040 ast.fileName = incFileName;
1041
1042 const oldFilePath = ctx.filePath;
1043 const oldFileName = ctx.fileName;
1044 ctx.filePath = incFilePath;
1045 ctx.fileName = incFileName;
1046
1047 scopes.push({});
1048 createRefs(ctx, ast.block);
1049 scopes.pop();
1050
1051 ctx.filePath = oldFilePath;
1052 ctx.fileName = oldFileName;
1053 } else if (ast.type == "VARIABLE") {
1054 ast.refId = name2ref(ast.name);
1055 }
1056
1057 function define(name, entry) {
1058 if (scopes[scopes.length-1][name]) return ctx.throwError(ast, `Name already defined: ${name}`);
1059 for (let i=scopes.length-2; i>=0; i--) {
1060 if (scopes[i][name]) ctx.logWarning(ast, `Shadowing variable: ${name}`);
1061 }
1062 const refId = ctx.refs.length;
1063 entry.refId = refId;
1064 ctx.refs.push(entry);
1065 scopes[scopes.length-1][name] = refId;
1066 return refId;
1067 }
1068
1069 function name2ref(n) {
1070 for (let i=scopes.length-1; i>=0; i--) {
1071 if (typeof scopes[i][n] !== "undefined") return scopes[i][n];
1072 }
1073 return -1;
1074 }
1075
1076
1077 });
1078
1079}
1080
1081