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.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("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("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 console.log(ev.v.toString());
562 return;
563 }
564 if (ast.name == "assert") {
565 const v = exec(ctx, ast.params[0]);
566 const ev = val(ctx, v, ast);
567 if (ev.isZero()) return ctx.throwError(ast, "Assertion failed");
568 }
569
570 const fnc = ctx.functions[ast.name];
571
572 if (!fnc) return ctx.throwError("Function not defined");
573
574 const paramValues = [];
575 for (let i=0; i< ast.params.length; i++) {
576 const v = exec(ctx, ast.params[i]);
577 if (ctx.error) return;
578
579 paramValues.push(v);
580 }
581
582 if (ast.params.length != paramValues.length) ctx.throwError(ast, "Invalid Number of parameters");
583
584 const oldFileName = ctx.fileName;
585 const oldFilePath = ctx.filePath;
586 const oldRefs = ctx.refs;
587
588 ctx.fileName = fnc.fileName;
589 ctx.filePath = fnc.filePath;
590 ctx.refs = [];
591
592 const scope = {};
593 for (let i=0; i< fnc.params.length; i++) {
594 const entry = clone(paramValues[i]);
595 entry.refId = i;
596 scope[fnc.params[i]] = i;
597 ctx.refs.push(entry);
598 }
599
600 createRefs(ctx, fnc.block, scope);
601
602 execBlock(ctx, fnc.block);
603
604 const res = ctx.returnValue;
605 ctx.returnValue = null;
606
607 ctx.fileName = oldFileName;
608 ctx.filePath = oldFilePath;
609 ctx.refs = oldRefs;
610
611 return res;
612}
613
614function execReturn(ctx, ast) {
615 ctx.returnValue = exec(ctx, ast.value);
616 return;
617}
618
619
620function execVariable(ctx, ast) {
621
622 const v = ctx.refs[ast.refId];
623
624 const sels = [];
625 for (let i=0; i< ast.selectors.length; i++) {
626 const sel = exec(ctx, ast.selectors[i]);
627 if (ctx.error) return;
628 if (sel.s[0] != 1) return ctx.throwError(ast, "Variable selector cannot be an array");
629 if (sel.v[0].t != "N") return NQVAL;
630 sels.push(sel.v[0].v.toJSNumber());
631 }
632
633 let o = 0;
634 let s = v.s[0];
635
636 if (sels.length > v.s.length-2) return ctx.throwError(ast, "Too many selectors");
637 for (let i=0; i<sels.length; i++) {
638 if (sels[i] >= v.s[i] / v.s[i+1]) return ctx.throwError(ast, "Out of Range");
639 if (sels[i] < 0 ) return ctx.throwError(ast, "selector negative");
640 o += sels[i] * v.s[i+1];
641 s = v.s[i+1];
642 }
643
644 if (v.t == "V") {
645 return {
646 t: "V",
647 s: v.s.slice(sels.length),
648 v: v.v.slice(o, o+s)
649 };
650 } else if (v.t == "S") {
651 return {
652 t: "S",
653 s: v.s.slice(sels.length),
654 sIdx: o + v.sIdx
655 };
656 } else if (v.t == "C") {
657 return {
658 t: "C",
659 s: v.s.slice(sels.length),
660 cIdx: o
661 };
662 } else {
663 assert(false);
664 }
665}
666
667
668
669
670function execPin(ctx, ast) {
671 const selsC = [];
672 for (let i=0; i< ast.component.selectors.length; i++) {
673 const sel = exec(ctx, ast.component.selectors[i]);
674 if (ctx.error) return;
675
676 if (sel.s[0] != 1) return ctx.throwError(ast, "Component selector cannot be an array");
677 if (sel.v[0].t != "N") return NQVAL;
678 selsC.push(sel.v[0].v.toJSNumber());
679 }
680
681 const cIdx = ctx.getComponentIdx(ast.component.name, selsC);
682 if (cIdx<0) return ctx.throwError(ast.component, "Component does not exists: "+ast.component.name);
683
684 const selsP = [];
685 for (let i=0; i< ast.pin.selectors.length; i++) {
686 const sel = exec(ctx, ast.pin.selectors[i]);
687 if (ctx.error) return;
688 if (sel.s[0] != 1) return ctx.throwError(ast, "Signal selector cannot be an array");
689 if (sel.v[0].t != "N") return NQVAL;
690 selsP.push(sel.v[0].v.toJSNumber());
691 }
692 const sIdx = ctx.components[cIdx].names.getSignalIdx(ast.pin.name, selsP);
693
694 if (sIdx<0) return ctx.throwError(ast, "Signal not defined:" + buildFullName() );
695 return {
696 t: "S",
697 sIdx: sIdx,
698 s: utils.accSizes(ctx.components[cIdx].names.o[ast.pin.name].sizes).slice(selsP.length)
699 };
700
701 function buildFullName() {
702 return ast.component.name + sels2str(selsC) + "." + ast.pin.name + sels2str(selsP);
703 }
704
705 function sels2str(sels) {
706 let S = "";
707 for (let i=0; i< sels.length; i++) {
708 S += "[" + sels[i] + "]";
709 }
710 return S;
711 }
712}
713
714function execLoop(ctx, ast) {
715
716 if (ast.init) {
717 exec(ctx, ast.init);
718 if (ctx.error) return;
719 }
720
721 let v = exec(ctx, ast.condition);
722 if (ctx.error) return;
723 if (v.s[0] != 1) return ctx.throwError(ast.condition, "Condition in loop cannot be an array");
724 if (v.v[0].t != "N") {
725 iterateAST(ast, (ast) => {
726 if (ast.type == "OP") {
727 if (["==>", "<==", "==="].indexOf(ast.op) >= 0) {
728 return ctx.throwError(ast.condition, "Constraint inside a calculating block");
729 }
730 }
731 });
732
733 // Assert no constraints
734 return;
735 }
736
737 while ((!v.v[0].v.isZero())&&(!ctx.returnValue)) {
738 exec(ctx, ast.body);
739 if (ctx.error) return;
740
741 if (ast.step) {
742 exec(ctx, ast.step);
743 if (ctx.error) return;
744 }
745
746 v = exec(ctx, ast.condition);
747 if (ctx.error) return;
748 if (v.s[0] != 1) return ctx.throwError(ast.condition, "Condition in loop cannot be an array");
749 if (v.v[0].t != "N") return ctx.throwError(ast.condition, "Condition result not a number");
750 }
751}
752
753function execCompute(ctx, ast) {
754 iterateAST(ast, (ast) => {
755 if (ast.type == "OP") {
756 if (["==>", "<==", "==="].indexOf(ast.op) >= 0) {
757 return ctx.throwError(ast.condition, "Constraint inside a calculating block");
758 }
759 }
760 });
761}
762
763function execIf(ctx, ast) {
764 let v = exec(ctx, ast.condition);
765 if (ctx.error) return;
766 if (v.s[0] != 1) return ctx.throwError(ast.condition, "Condition cannot be an array");
767 if (v.v[0].t != "N") {
768 iterateAST(ast, (ast) => {
769 if (ast.type == "OP") {
770 if (["==>", "<==", "==="].indexOf(ast.op) >= 0) {
771 return ctx.throwError(ast.condition, "Constraint inside a calculating block");
772 }
773 }
774 });
775
776 // Assert no constraints
777 return;
778 }
779
780 if (!v.v[0].v.isZero()) {
781 exec(ctx, ast.then);
782 } else {
783 if (ast.else) {
784 exec(ctx, ast.else);
785 }
786 }
787}
788
789
790function execTerCon(ctx, ast) {
791 let v = exec(ctx, ast.values[0]);
792 if (ctx.error) return;
793 if (v.s[0] != 1) return ctx.throwError(ast.condition, "Condition cannot be an array");
794 if (v.v[0].t != "N") {
795 iterateAST(ast, (ast) => {
796 if (ast.type == "OP") {
797 if (["==>", "<==", "==="].indexOf(ast.op) >= 0) {
798 return ctx.throwError(ast.condition, "Constraint inside a calculating block");
799 }
800 }
801 });
802
803 // Assert no constraints
804 return NQVAL;
805 }
806
807 if (!v.v[0].v.isZero()) {
808 return exec(ctx, ast.values[1]);
809 } else {
810 return exec(ctx, ast.values[2]);
811 }
812}
813
814function execOp(ctx, ast, op, nOps) {
815 const operands = [];
816 for (let i=0; i<nOps; i++) {
817 const a = exec(ctx, ast.values[i]);
818 if (ctx.error) return;
819 const aV = val(ctx, a, ast.values[i]);
820 operands.push(aV);
821 }
822
823 return {
824 t: "V",
825 s: [1,0],
826 v: [ctx.lc[op](...operands)]
827 };
828}
829
830
831function execOpOp(ctx, ast, op, lr) {
832
833 if (ast.values[0].type != "VARIABLE") return ctx.throwError(ast, "incrementing a non variable");
834 if (!utils.isDefined(ast.values[0].refId)) return ctx.throwError(ast, "Assigning to a non variable");
835 const left = ctx.refs[ast.values[0].refId];
836 if (left.t != "V") return ctx.throwError(ast, `Only variables can use ${ast.op} operations`);
837 let leftSels = [];
838 if (ast.values[0].selectors) {
839 for (let i=0; i< ast.values[0].selectors.length; i++) {
840 const sel = exec(ctx, ast.values[0].selectors[i]);
841 if (ctx.error) return;
842
843 if (sel.s[0] != 1) return ctx.throwError(ast, "Selector cannot be an array");
844 if (sel.v[0].t != "N") return {t: "NQ"};
845
846 leftSels.push( sel.v[0].v.toJSNumber() );
847 }
848 }
849 if (!left.s) return ctx.throwError(ast, "variable. not defined yet");
850
851 let o = 0;
852 let s = left.s[0];
853 for (let i=0; i<leftSels.length; i++) {
854 o += leftSels[i]*left.s[i+1];
855 s = left.s[i+1];
856 }
857 if (s != 1) return ctx.throwError(ast, `invalid operator ${ast.op} in an array`);
858 const resBefore = left.v[o];
859
860 let right;
861 if (ast.values[1]) {
862 const rightRef = exec(ctx, ast.values[1]);
863 if (ctx.error) return;
864 right = val(ctx, rightRef);
865 } else {
866 right = {t:"N", v: ctx.field.one};
867 }
868
869 const resAfter = ctx.lc[op](resBefore, right);
870 left.v[o] = resAfter;
871
872 if (lr == "RIGHT") {
873 return {
874 t: "V",
875 s: [1,0],
876 v: [resBefore]
877 };
878 } else {
879 return {
880 t: "V",
881 s: [1,0],
882 v: [resAfter]
883 };
884 }
885}
886
887// Extract the value of a reference.
888// Must be a single value. (cannot be an array)
889// The result is an LCAlgebra compatible object {t: ["N"|"LC"|"QEX"|"NQ"], [coefs|v|a,b,c]}
890function val(ctx, a, ast) {
891 if (a.s[0] != 1) return ctx.throwError(ast, "Array not allowed");
892 if (a.t=="V") {
893 return a.v[0];
894 } else if (a.t=="S") {
895 const res = {
896 t: "LC",
897 coefs: {}
898 };
899 let sIdx = a.sIdx;
900 while (ctx.signals[sIdx].e >= 0) sIdx = ctx.signals[sIdx].e;
901 res.coefs[sIdx] = ctx.field.one;
902 return res;
903 } else {
904 ctx.throwError(ast, "Invalid type: " + a.t);
905 }
906}
907
908function execConstrain(ctx, ast) {
909 ast.fileName = ctx.fileName;
910 ast.filePath = ctx.filePath;
911 const a = exec(ctx, ast.values[0]);
912 if (ctx.error) return;
913 const aV = val(ctx, a, ast.values[0]);
914 const b = exec(ctx, ast.values[1]);
915 if (ctx.error) return;
916 const bV = val(ctx, b, ast.values[1]);
917
918 const res = ctx.lc.sub(aV,bV);
919 if (res.type == "NQ") return ctx.throwError(ast, "Non Quadratic constraint");
920
921 if (!ctx.lc.isZero(res)) {
922 ctx.constraints.push(ctx.lc.toQEX(res));
923 if ((ctx.constraints.length % 10000 == 0)&&(ctx.constraints.length>0)) console.log("Constraints: " + ctx.constraints.length);
924 }
925
926 return a;
927}
928
929function execSignalAssignConstrain(ctx, ast) {
930 const v = execAssignement(ctx,ast);
931 if (ctx.error) return;
932 execConstrain(ctx, ast);
933 if (ctx.error) return;
934 return v;
935}
936
937function execInclude(ctx, ast) {
938 if (ast.block) {
939
940 const oldFilePath = ctx.filePath;
941 const oldFileName = ctx.fileName;
942
943 ctx.filePath = ast.filePath;
944 ctx.fileName = ast.fileName;
945
946 exec(ctx, ast.block); // Process only one.
947
948 ctx.filePath = oldFilePath;
949 ctx.fileName = oldFileName;
950 }
951}
952
953function execArray(ctx, ast) {
954 const res = {t: "V", v:[]};
955
956
957 let subSize = null;
958
959 for (let i=0; i<ast.values.length; i++) {
960 const e = exec(ctx, ast.values[i]);
961 if (i==0) {
962 subSize=e.s;
963 } else {
964 if (!utils.sameSizes(subSize, e.s)) return ctx.throwError(ast, "Array mus be homogeneous");
965 }
966 if (e.t == "V") {
967 for (let j=0; j<e.v.length;j++) res.v.push(e.v[j]);
968 } else if (e.t == "S") {
969 for (let j=0; j<e.v.length;j++) {
970 const sv = {t: "LC", coefs: {}};
971 sv.coefs[e.sIdx+j] = ctx.field.one;
972 res.v.push(sv);
973 }
974 } else {
975 return ctx.throwError(ast, "Type not allowed in array");
976 }
977 }
978
979 res.s = [ast.values.length*subSize[0], ...subSize];
980
981 return res;
982}
983
984function createRefs(ctx, ast, scope) {
985 const scopeLabels = [];
986 const scopes = scope ? [scope] : [{}];
987 iterateAST(ast, (ast, level) => {
988 while ((scopeLabels.length>0)&&(!level.startsWith(scopeLabels[scopeLabels.length-1]))) {
989 scopes.pop();
990 scopeLabels.pop();
991 }
992 if (ast.type == "DECLARE") {
993 if (ast.declareType == "COMPONENT") {
994 ast.refId = define(ast.name.name, {t: "C"});
995 } else if ((ast.declareType == "SIGNALIN")||
996 (ast.declareType == "SIGNALOUT")||
997 (ast.declareType == "SIGNAL")) {
998 ast.refId = define(ast.name.name, {t: "S"});
999 } else if (ast.declareType == "VARIABLE") {
1000 ast.refId = define(ast.name.name, {t: "V", s: null});
1001 } else {
1002 return ctx.throwError(ast, "Invalid declaration: " + ast.declareType);
1003 }
1004 } else if (["BLOCK", "FOR", "IF", "WHILE", "COMPUTE"].indexOf(ast.type) >= 0) {
1005 scopeLabels.push(level);
1006 scopes.push({});
1007 } else if (ast.type == "TEMPLATEDEF") {
1008 ast.refId = define(ast.name, {t: "T"});
1009 } else if (ast.type == "FUNCTIONDEF") {
1010 ast.refId = define(ast.name, {t: "F"});
1011 } else if (ast.type == "INCLUDE") {
1012 const incFileName = path.resolve(ctx.filePath, ast.file);
1013 const incFilePath = path.dirname(incFileName);
1014
1015 ctx.includedFiles = ctx.includedFiles || [];
1016 if (ctx.includedFiles[incFileName]) {
1017 ctx.bloc = null; // Include already included...
1018 return;
1019 }
1020
1021 const src = fs.readFileSync(incFileName, "utf8");
1022
1023 if (!src) return ctx.throwError(ast, "Include file not found: "+incFileName);
1024
1025 ctx.includedFiles[incFileName] = src.split("\n");
1026
1027 ast.block = parser.parse(src);
1028 ast.filePath = incFilePath;
1029 ast.fileName = incFileName;
1030
1031 const oldFilePath = ctx.filePath;
1032 const oldFileName = ctx.fileName;
1033 ctx.filePath = incFilePath;
1034 ctx.fileName = incFileName;
1035
1036 scopes.push({});
1037 createRefs(ctx, ast.block);
1038 scopes.pop();
1039
1040 ctx.filePath = oldFilePath;
1041 ctx.fileName = oldFileName;
1042 } else if (ast.type == "VARIABLE") {
1043 ast.refId = name2ref(ast.name);
1044 }
1045
1046 function define(name, entry) {
1047 if (scopes[scopes.length-1][name]) return ctx.throwError(ast, `Name already defined: ${name}`);
1048 for (let i=scopes.length-2; i>=0; i--) {
1049 if (scopes[i][name]) ctx.logWarning(ast, `Shadowing variable: ${name}`);
1050 }
1051 const refId = ctx.refs.length;
1052 entry.refId = refId;
1053 ctx.refs.push(entry);
1054 scopes[scopes.length-1][name] = refId;
1055 return refId;
1056 }
1057
1058 function name2ref(n) {
1059 for (let i=scopes.length-1; i>=0; i--) {
1060 if (typeof scopes[i][n] !== "undefined") return scopes[i][n];
1061 }
1062 return -1;
1063 }
1064
1065
1066 });
1067
1068}
1069
1070