circom/src/exec.js
src/exec.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
20const path = require("path");
21const fs = require("fs");
22
23const utils = require("./utils");
24
25const bigInt = require("big-integer");
26const __P__ = new bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
27const __MASK__ = new bigInt(2).pow(253).minus(1);
28
29const lc = require("./lcalgebra");
30const parser = require("../parser/jaz.js").parser;
31
32/* TODO: Add lines information
33
34function setLines(dst, first, last) {
35 last = last || first;
36 dst.first_line = first.first_line;
37 dst.first_column = first.first_column;
38 dst.last_line = last.last_line;
39 dst.last_column = last.last_column;
40}
41*/
42
43module.exports = exec;
44
45
46function exec(ctx, ast) {
47 if (!ast) {
48 return error(ctx, ast, "Null AST");
49 }
50 if ((ast.type == "NUMBER") || (ast.type == "LINEARCOMBINATION") || (ast.type =="SIGNAL") || (ast.type == "QEQ")) {
51 return ast;
52 } else if (ast.type == "VARIABLE") {
53 return execVariable(ctx, ast);
54 } else if (ast.type == "PIN") {
55 return execPin(ctx, ast);
56 } else if (ast.type == "OP") {
57 if (ast.op == "=") {
58 return execVarAssignement(ctx, ast);
59 } else if (ast.op == "<--") {
60 return execSignalAssign(ctx, ast);
61 } else if (ast.op == "<==") {
62 return execSignalAssignConstrain(ctx, ast);
63 } else if (ast.op == "===") {
64 return execConstrain(ctx, ast);
65 } else if (ast.op == "+=") {
66 return execVarAddAssignement(ctx, ast);
67 } else if (ast.op == "*=") {
68 return execVarMulAssignement(ctx, ast);
69 } else if (ast.op == "+") {
70 return execAdd(ctx, ast);
71 } else if (ast.op == "-") {
72 return execSub(ctx, ast);
73 } else if (ast.op == "UMINUS") {
74 return execUMinus(ctx, ast);
75 } else if (ast.op == "*") {
76 return execMul(ctx, ast);
77 } else if (ast.op == "%") {
78 return execMod(ctx, ast);
79 } else if (ast.op == "PLUSPLUSRIGHT") {
80 return execPlusPlusRight(ctx, ast);
81 } else if (ast.op == "PLUSPLUSLEFT") {
82 return execPlusPlusLeft(ctx, ast);
83 } else if (ast.op == "MINUSMINUSRIGHT") {
84 return execMinusMinusRight(ctx, ast);
85 } else if (ast.op == "MINUSMINUSLEFT") {
86 return execMinusMinusLeft(ctx, ast);
87 } else if (ast.op == "/") {
88 return execDiv(ctx, ast);
89 } else if (ast.op == "\\") {
90 return execIDiv(ctx, ast);
91 } else if (ast.op == "**") {
92 return execExp(ctx, ast);
93 } else if (ast.op == "&") {
94 return execBAnd(ctx, ast);
95 } else if (ast.op == "|") {
96 return execBOr(ctx, ast);
97 } else if (ast.op == "^") {
98 return execBXor(ctx, ast);
99 } else if (ast.op == "~") {
100 return execBNot(ctx, ast);
101 } else if (ast.op == "&&") {
102 return execAnd(ctx, ast);
103 } else if (ast.op == "||") {
104 return execOr(ctx, ast);
105 } else if (ast.op == "!") {
106 return execLNot(ctx, ast);
107 } else if (ast.op == "<<") {
108 return execShl(ctx, ast);
109 } else if (ast.op == ">>") {
110 return execShr(ctx, ast);
111 } else if (ast.op == "<") {
112 return execLt(ctx, ast);
113 } else if (ast.op == ">") {
114 return execGt(ctx, ast);
115 } else if (ast.op == "<=") {
116 return execLte(ctx, ast);
117 } else if (ast.op == ">=") {
118 return execGte(ctx, ast);
119 } else if (ast.op == "==") {
120 return execEq(ctx, ast);
121 } else if (ast.op == "!=") {
122 return execNeq(ctx, ast);
123 } else if (ast.op == "?") {
124 return execTerCon(ctx, ast);
125 } else {
126 error(ctx, ast, "Invalid operation: " + ast.op);
127 }
128 } else if (ast.type == "DECLARE") {
129 if (ast.declareType == "COMPONENT") {
130 return execDeclareComponent(ctx, ast);
131 } else if ((ast.declareType == "SIGNALIN")||
132 (ast.declareType == "SIGNALOUT")||
133 (ast.declareType == "SIGNAL")) {
134 return execDeclareSignal(ctx, ast);
135 } else if (ast.declareType == "VARIABLE") {
136 return execDeclareVariable(ctx, ast);
137 } else {
138 error(ctx, ast, "Invalid declaration: " + ast.declareType);
139 }
140 } else if (ast.type == "FUNCTIONCALL") {
141 return execFunctionCall(ctx, ast);
142 } else if (ast.type == "BLOCK") {
143 return execBlock(ctx, ast);
144 } else if (ast.type == "COMPUTE") {
145 return ;
146 } else if (ast.type == "FOR") {
147 return execFor(ctx, ast);
148 } else if (ast.type == "WHILE") {
149 return execWhile(ctx, ast);
150 } else if (ast.type == "IF") {
151 return execIf(ctx, ast);
152 } else if (ast.type == "RETURN") {
153 return execReturn(ctx, ast);
154 } else if (ast.type == "TEMPLATEDEF") {
155 return execTemplateDef(ctx, ast);
156 } else if (ast.type == "FUNCTIONDEF") {
157 return execFunctionDef(ctx, ast);
158 } else if (ast.type == "INCLUDE") {
159 return execInclude(ctx, ast);
160 } else if (ast.type == "ARRAY") {
161 return execArray(ctx, ast);
162 } else {
163 error(ctx, ast, "Invalid AST node type: " + ast.type);
164 }
165}
166
167function error(ctx, ast, errStr) {
168 ctx.error = {
169 pos: {
170 first_line: ast.first_line,
171 first_column: ast.first_column,
172 last_line: ast.last_line,
173 last_column: ast.last_column
174 },
175 errStr: errStr,
176 errFile: ctx.fileName,
177 ast: ast,
178 message: errStr
179 };
180}
181
182function iterateSelectors(ctx, sizes, baseName, fn) {
183 if (sizes.length == 0) {
184 return fn(baseName);
185 }
186 const res = [];
187 for (let i=0; i<sizes[0]; i++) {
188 res.push(iterateSelectors(ctx, sizes.slice(1), baseName+"["+i+"]", fn));
189 if (ctx.error) return null;
190 }
191 return res;
192}
193
194
195function getScope(ctx, name, selectors) {
196
197 const sels = [];
198 if (selectors) {
199 for (let i=0; i< selectors.length; i++) {
200 const idx = exec(ctx, selectors[i]);
201 if (ctx.error) return;
202
203 if (idx.type != "NUMBER") return error(ctx, selectors[i], "expected a number");
204 sels.push( idx.value.toJSNumber() );
205 }
206 }
207
208
209 function select(v, sels) {
210 if (v.type == "SIGNAL") {
211 return reduce(v, sels, "sIdx");
212 } else if (v.type == "COMPONENT") {
213 return reduce(v, sels, "cIdx");
214 } else {
215 const s = sels || [];
216 if (s.length == 0) return v;
217 return select(v[s[0]], s.slice(1));
218 }
219 }
220
221 for (let i=ctx.scopes.length-1; i>=0; i--) {
222 if (ctx.scopes[i][name]) return select(ctx.scopes[i][name].value, sels);
223 }
224 return null;
225
226 function reduce(v, _sels, idxName) {
227 let sels = _sels || [];
228 let sizes = v.sizes || [];
229
230 let accSizes = [1];
231 for (let i=sizes.length-1; i>0; i--) {
232 accSizes = [accSizes[0]*sizes[i], ...accSizes];
233 }
234 const res = Object.assign({}, v);
235 res.sizes = sizes.slice(sels.length);
236 for (let i=0; i<sels.length; i++) {
237 res[idxName] += sels[i]*accSizes[i];
238 }
239 return res;
240 }
241}
242
243function getScopeRef(ctx, name, selectors) {
244
245 const sels = [];
246 if (selectors) {
247 for (let i=0; i< selectors.length; i++) {
248 const idx = exec(ctx, selectors[i]);
249 if (ctx.error) return;
250
251 if (idx.type != "NUMBER") return error(ctx, selectors[i], "expected a number");
252 sels.push( idx.value.toJSNumber() );
253 }
254 }
255
256
257 function select(v, s, t) {
258 s = s || [];
259 if (s.length == 0) return [v, sels, t];
260 return select(v[s[0]], s.slice(1), t);
261 }
262
263 for (let i=ctx.scopes.length-1; i>=0; i--) {
264 if (ctx.scopes[i][name]) return select(ctx.scopes[i][name].value, sels, ctx.scopes[i][name].type);
265 }
266 return [null, [], ""];
267}
268
269
270function setScopeRef(ctx, name, sels, value) {
271 let l = getScopeLevel(ctx, name);
272 if (l==-1) l= ctx.scopes.length-1;
273
274 if (sels.length == 0) {
275 ctx.scopes[l][name].value = value;
276 } else {
277 setScopeArray(ctx.scopes[l][name].value, sels);
278 }
279
280 function setScopeArray(a, sels) {
281 if (sels.length == 1) {
282 a[sels[0]] = value;
283 } else {
284 setScopeArray(a[sels[0]], sels.slice(1));
285 }
286 }
287}
288
289function getScopeLevel(ctx, name) {
290 for (let i=ctx.scopes.length-1; i>=0; i--) {
291 if (ctx.scopes[i][name]) return i;
292 }
293 return -1;
294}
295
296function execBlock(ctx, ast) {
297 for (let i=0; i<ast.statements.length; i++) {
298 exec(ctx, ast.statements[i]);
299 if (ctx.returnValue) return;
300 if (ctx.error) return;
301 }
302}
303
304function execTemplateDef(ctx, ast) {
305 const scope = ctx.scopes[0]; // Lets put templates always in top scope.
306 // const scope = ctx.scopes[ctx.scopes.length-1];
307 if (getScope(ctx, ast.name)) {
308 return error(ctx, ast, "Name already exists: "+ast.name);
309 }
310 scope[ast.name] = {
311 type: "TEMPLATE",
312 value: {
313 type: "TEMPLATE",
314 params: ast.params,
315 block: ast.block,
316 fileName: ctx.fileName,
317 filePath: ctx.filePath,
318 scopes: copyScope(ctx.scopes)
319 }
320 };
321 ctx.templates[ast.name] = {
322 block: ast.block,
323 params: ast.params,
324 fileName: ctx.fileName,
325 filePath: ctx.filePath,
326 };
327}
328
329function execFunctionDef(ctx, ast) {
330 const scope = ctx.scopes[0]; // Lets put functions always in top scope.
331 // const scope = ctx.scopes[ctx.scopes.length-1];
332 if (getScope(ctx, ast.name)) {
333 return error(ctx, ast, "Name already exists: "+ast.name);
334 }
335 ctx.functionParams[ast.name] = ast.params;
336 scope[ast.name] = {
337 type: "FUNCTION",
338 value: {
339 type: "FUNCTION",
340 params: ast.params,
341 block: ast.block,
342 fileName: ctx.fileName,
343 filePath: ctx.filePath,
344 scopes: copyScope(ctx.scopes)
345 }
346 };
347 ctx.functions[ast.name] = {
348 block: ast.block,
349 params: ast.params,
350 fileName: ctx.fileName,
351 filePath: ctx.filePath
352 };
353}
354
355
356function execDeclareComponent(ctx, ast) {
357 const scope = ctx.scopes[ctx.scopes.length-1];
358
359 if (ast.name.type != "VARIABLE") return error(ctx, ast, "Invalid component name");
360 if (getScope(ctx, ast.name.name)) return error(ctx, ast, "Name already exists: "+ast.name.name);
361
362 const sizes=[];
363 for (let i=0; i< ast.name.selectors.length; i++) {
364 const size = exec(ctx, ast.name.selectors[i]);
365 if (ctx.error) return;
366
367 if (size.type != "NUMBER") return error(ctx, ast.name.selectors[i], "expected a number");
368
369 sizes.push( size.value.toJSNumber() );
370 }
371
372 const cIdx = ctx.addComponent(ast.name.name, sizes);
373
374 scope[ast.name.name] = {
375 type: "COMPONENT",
376 value: {
377 type: "COMPONENT",
378 sizes: sizes,
379 cIdx: Array.isArray(cIdx) ? cIdx[0] : cIdx
380 }
381 };
382
383 return {
384 type: "VARIABLE",
385 name: ast.name.name,
386 selectors: []
387 };
388}
389
390function execInstantiateComponet(ctx, vr, fn, sels) {
391
392 if (vr.type != "VARIABLE") return error(ctx, fn, "Left hand instatiate component must be a variable");
393 if (fn.type != "FUNCTIONCALL") return error(ctx, fn, "Right type of instantiate component must be a function call");
394
395 const componentName = vr.name;
396 const templateName = fn.name;
397
398 const scopeLevel = getScopeLevel(ctx, templateName);
399 if (scopeLevel == -1) return error(ctx,fn, "Invalid Template");
400 const template = getScope(ctx, templateName);
401
402 if (template.type != "TEMPLATE") return error(ctx, fn, "Invalid Template");
403
404
405 const paramValues = [];
406 for (let i=0; i< fn.params.length; i++) {
407 const v = exec(ctx, fn.params[i]);
408 if (ctx.error) return;
409
410 paramValues.push(v);
411 }
412 if (template.params.length != paramValues.length) return error(ctx, fn, "Invalid Number of parameters");
413
414
415 const vComp = getScope(ctx, componentName);
416 if (vComp.type != "COMPONENT") return error(ctx, fn, "Assigning to a non component");
417 const cIdx = vComp.cIdx;
418 if (cIdx == -1) return error(ctx, fn, "Component not defined");
419
420
421 const aSizes = utils.accSizes(vComp.sizes);
422 let o=0;
423 for (let i=0; i<sels.length; i++) o += sels[i] * aSizes[i+1];
424 for (let i=o; i<o+aSizes[sels.length]; i++) {
425 instantiateComponent(cIdx+i);
426 }
427
428 function instantiateComponent(cIdx) {
429
430 function extractValue(v) {
431 if (Array.isArray(v)) {
432 return v.map(extractValue);
433 } else {
434 return v.value.toString();
435 }
436 }
437
438 if (ctx.components[cIdx]) return error(ctx, fn, "Component already instantiated");
439
440 const oldComponent = ctx.currentComponent;
441 const oldFileName = ctx.fileName;
442 const oldFilePath = ctx.filePath;
443 const oldMain = ctx.main;
444
445 if ((componentName == "main")&&(ctx.currentComponent==-1)) {
446 ctx.main=true;
447 } else {
448 ctx.main=false;
449 }
450
451 ctx.currentComponent = cIdx;
452
453 ctx.components[cIdx] = {
454 params: {},
455 names: ctx.newTableName(),
456 nInSignals: 0
457 };
458
459 const oldScopes = ctx.scopes;
460
461 ctx.scopes = oldScopes.slice(0, scopeLevel+1);
462
463 if (template.params.length != paramValues.length) return error(ctx, fn, "Invalid number of parameters: " + templateName);
464
465 const scope = {};
466 for (let i=0; i< template.params.length; i++) {
467 scope[template.params[i]] = {
468 type: "VARIABLE",
469 value: paramValues[i]
470 };
471 ctx.components[cIdx].params[template.params[i]] = extractValue(paramValues[i]);
472 }
473
474 ctx.components[cIdx].template = templateName;
475 ctx.fileName = template.fileName;
476 ctx.filePath = template.filePath;
477 ctx.scopes = copyScope( template.scopes );
478 ctx.scopes.push(scope);
479
480
481 execBlock(ctx, template.block);
482
483 ctx.fileName = oldFileName;
484 ctx.filePath = oldFilePath;
485 ctx.currentComponent = oldComponent;
486 ctx.main = oldMain;
487 ctx.scopes = oldScopes;
488 }
489}
490
491function execFunctionCall(ctx, ast) {
492
493 if (ast.name == "log") {
494 const v = exec(ctx, ast.params[0]);
495 console.log(v.value.toString());
496 return;
497 }
498
499 const scopeLevel = getScopeLevel(ctx, ast.name);
500 if (scopeLevel == -1) return error(ctx, ast, "Function not defined: " + ast.name);
501 const fnc = getScope(ctx, ast.name);
502
503 if (fnc.type != "FUNCTION") return error(ctx, ast, "Not a function: " + ast.name);
504
505 const paramValues = [];
506 for (let i=0; i< ast.params.length; i++) {
507 const v = exec(ctx, ast.params[i]);
508 if (ctx.error) return;
509
510 paramValues.push(v);
511 }
512
513 if (ast.params.length != paramValues.length) error(ctx, ast, "Invalid Number of parameters");
514
515 const oldFileName = ctx.fileName;
516 const oldFilePath = ctx.filePath;
517
518 const oldScopes = ctx.scopes;
519
520 ctx.scopes = oldScopes.slice(0, scopeLevel+1);
521
522 const scope = {};
523 for (let i=0; i< fnc.params.length; i++) {
524 scope[fnc.params[i]] = {
525 type: "VARIABLE",
526 value: paramValues[i]
527 };
528 }
529
530 ctx.fileName = fnc.fileName;
531 ctx.filePath = fnc.filePath;
532 ctx.scopes = copyScope( fnc.scopes );
533 ctx.scopes.push(scope);
534
535 execBlock(ctx, fnc.block);
536
537 const res = ctx.returnValue;
538 ctx.returnValue = null;
539
540 ctx.fileName = oldFileName;
541 ctx.filePath = oldFilePath;
542 ctx.scopes = oldScopes;
543
544 return res;
545}
546
547function execReturn(ctx, ast) {
548 ctx.returnValue = exec(ctx, ast.value);
549 return;
550}
551
552function execDeclareSignal(ctx, ast) {
553 const scope = ctx.scopes[ctx.scopes.length-1];
554
555 if (ast.name.type != "VARIABLE") return error(ctx, ast, "Invalid component name");
556 if (getScope(ctx, ast.name.name)) return error(ctx, ast, "Name already exists: "+ast.name.name);
557
558 let totalSize = 1;
559 const sizes=[];
560 for (let i=0; i< ast.name.selectors.length; i++) {
561 const size = exec(ctx, ast.name.selectors[i]);
562 if (ctx.error) return;
563
564 if (size.type != "NUMBER") return error(ctx, ast.name.selectors[i], "expected a number");
565 const s = size.value.toJSNumber();
566 totalSize = totalSize * s;
567 sizes.push( s );
568 }
569
570 let sIdx = ctx.addSignal(ast.name.name, sizes);
571 if (!Array.isArray(sIdx)) sIdx = [sIdx, sIdx+1];
572 for (let i=sIdx[0]; i<sIdx[1]; i++) {
573 ctx.signals[i] = {
574 o: 0,
575 e: -1
576 };
577
578 if (ast.declareType == "SIGNALIN") {
579 ctx.signals[i].o |= ctx.IN;
580 ctx.components[ctx.currentComponent].nInSignals+=1;
581 }
582 if (ast.declareType == "SIGNALOUT") {
583 ctx.signals[i].o |= ctx.OUT;
584 }
585 if (ast.private ) {
586 ctx.signals[i].o |= ctx.PRV;
587 }
588 if (ctx.main) {
589 ctx.signals[i].o |= ctx.MAIN;
590 }
591
592 // ctx.components[ctx.currentComponent].signals.push(i);
593 }
594 scope[ast.name.name] = {
595 type: "SIGNAL",
596 value: {
597 type: "SIGNAL",
598 sizes: sizes,
599 sIdx: sIdx[0]
600 }
601 };
602 return {
603 type: "VARIABLE",
604 name: ast.name.name,
605 selectors: []
606 };
607}
608
609function execDeclareVariable(ctx, ast) {
610 const scope = ctx.scopes[ctx.scopes.length-1];
611
612 if (ast.name.type != "VARIABLE") return error(ctx, ast, "Invalid linear combination name");
613 if (getScope(ctx, ast.name.name)) return error(ctx, ast, "Name already exists: "+ast.name.name);
614
615 const sizes=[];
616 for (let i=0; i< ast.name.selectors.length; i++) {
617 const size = exec(ctx, ast.name.selectors[i]);
618 if (ctx.error) return;
619
620 if (size.type != "NUMBER") return error(ctx, ast.name.selectors[i], "expected a number");
621 sizes.push( size.value.toJSNumber() );
622 }
623
624 scope[ast.name.name] = {
625 type: "VARIABLE",
626 value: iterateSelectors(ctx, sizes, "", function() {
627 return {
628 type: "NUMBER",
629 value: bigInt(0)
630 };
631 })
632 };
633
634 return {
635 type: "VARIABLE",
636 name: ast.name.name,
637 selectors: []
638 };
639}
640
641function execVariable(ctx, ast) {
642 let v;
643 try {
644 v = getScope(ctx, ast.name, ast.selectors);
645 } catch(err) {
646 console.log(JSON.stringify(ast, null,1));
647 }
648 if (ctx.error) return;
649
650 if (!v) return error(ctx, ast, "Variable not defined");
651
652 // If the signal has an assigned value (constant) just return the constant
653 if ((v.type == "SIGNAL") && (utils.isDefined(ctx.signals[v.sIdx].v))) {
654 return {
655 type: "NUMBER",
656 value: ctx.signals[v.sIdx].v
657 };
658 }
659 let res;
660 res=v;
661 return res;
662}
663
664function execPin(ctx, ast) {
665 const selsC = [];
666 for (let i=0; i< ast.component.selectors.length; i++) {
667 const sel = exec(ctx, ast.component.selectors[i]);
668 if (sel.type != "NUMBER") return error(ctx, ast.pin.selectors[i], "expected a number");
669 selsC.push(sel.value.toJSNumber());
670 }
671
672 const cIdx = ctx.getComponentIdx(ast.component.name, selsC);
673 if (cIdx<0) return error(ctx, ast.component, "Component does not exists: "+ast.component.name);
674
675 const selsP = [];
676 for (let i=0; i< ast.pin.selectors.length; i++) {
677 const sel = exec(ctx, ast.pin.selectors[i]);
678 if (sel.type != "NUMBER") return error(ctx, ast.pin.selectors[i], "expected a number");
679 selsP.push(sel.value.toJSNumber());
680 }
681 const sIdx = ctx.components[cIdx].names.getSignalIdx(ast.pin.name, selsP);
682
683 if (sIdx<0) error(ctx, ast, "Signal not defined:" + buildFullName() );
684 return {
685 type: "SIGNAL",
686 sIdx: sIdx
687 };
688
689 function buildFullName() {
690 return ast.component.name + sels2str(selsC) + "." + ast.pin.name + sels2str(selsP);
691 }
692
693 function sels2str(sels) {
694 let S = "";
695 for (let i=0; i< sels.length; i++) {
696 const sel = exec(ctx, ast.pin.selectors[i]);
697 if (sel.type != "NUMBER") return error(ctx, ast.pin.selectors[i], "expected a number");
698 S += "[" + sel.value.toString() + "]";
699 }
700 return S;
701 }
702}
703
704function execFor(ctx, ast) {
705
706 ctx.scopes.push({});
707 exec(ctx, ast.init);
708 if (ctx.error) return;
709
710 let v = exec(ctx, ast.condition);
711 if (ctx.error) return;
712
713 if (typeof v.value != "undefined") {
714 while ((v.value.neq(0))&&(!ctx.returnValue)) {
715 exec(ctx, ast.body);
716 if (ctx.error) return;
717
718 exec(ctx, ast.step);
719 if (ctx.error) return;
720
721 v = exec(ctx, ast.condition);
722 if (ctx.error) return;
723 }
724 }
725 ctx.scopes.pop();
726}
727
728function execWhile(ctx, ast) {
729 let v = exec(ctx, ast.condition);
730 if (ctx.error) return;
731
732 if (typeof v.value != "undefined") {
733 while ((v.value.neq(0))&&(!ctx.returnValue)) {
734 exec(ctx, ast.body);
735 if (ctx.error) return;
736
737 v = exec(ctx, ast.condition);
738 if (ctx.error) return;
739 }
740 }
741}
742
743function execIf(ctx, ast) {
744 let v = exec(ctx, ast.condition);
745 if (ctx.error) return;
746
747 if (typeof v.value != "undefined") {
748 if ((v.value.neq(0))&&(!ctx.returnValue)) {
749 exec(ctx, ast.then);
750 if (ctx.error) return;
751 } else {
752 if (ast.else) {
753 exec(ctx, ast.else);
754 if (ctx.error) return;
755 }
756 }
757 }
758}
759
760
761function execVarAssignement(ctx, ast) {
762 let v;
763 if (ast.values[0].type == "DECLARE") {
764 v = exec(ctx, ast.values[0]);
765 if (ctx.error) return;
766 } else {
767 v = ast.values[0];
768 }
769 const [num, sels, typ] = getScopeRef(ctx, v.name, v.selectors);
770 if (ctx.error) return;
771
772 if (typ == "COMPONENT") return execInstantiateComponet(ctx, v, ast.values[1], sels);
773
774 if ((typeof(num) != "object")||(num == null)) return error(ctx, ast, "Variable not defined");
775
776 if (ctx.error) return;
777 if ((typ == "SIGNAL")&&(ast.op.indexOf("=")>=0)) return error(ctx, ast, "Cannot assign to a signal with `=` use <-- or <== ops");
778 if ((["NUMBER", "COMPONENT"].indexOf(typ) >= 0 )&&(ast.op != "=")) return error(ctx, ast, `Cannot assign to a var with ${ast.op}. use = op`);
779
780 const res = exec(ctx, ast.values[1]);
781 if (ctx.error) return;
782
783 setScopeRef(ctx, v.name, sels, res);
784
785 return v;
786}
787
788function execLt(ctx, ast) {
789 const a = exec(ctx, ast.values[0]);
790 if (ctx.error) return;
791 if (a.type != "NUMBER") return { type: "NUMBER" };
792 const b = exec(ctx, ast.values[1]);
793 if (ctx.error) return;
794 if (b.type != "NUMBER") return { type: "NUMBER" };
795 if (!a.value || !b.value) return { type: "NUMBER" };
796 return {
797 type: "NUMBER",
798 value: a.value.lt(b.value) ? bigInt(1) : bigInt(0)
799 };
800}
801
802function execGt(ctx, ast) {
803 const a = exec(ctx, ast.values[0]);
804 if (ctx.error) return;
805 if (a.type != "NUMBER") return { type: "NUMBER" };
806 const b = exec(ctx, ast.values[1]);
807 if (ctx.error) return;
808 if (b.type != "NUMBER") return { type: "NUMBER" };
809 if (!a.value || !b.value) return { type: "NUMBER" };
810 return {
811 type: "NUMBER",
812 value: a.value.gt(b.value) ? bigInt(1) : bigInt(0)
813 };
814}
815
816function execLte(ctx, ast) {
817 const a = exec(ctx, ast.values[0]);
818 if (ctx.error) return;
819 if (a.type != "NUMBER") return { type: "NUMBER" };
820 const b = exec(ctx, ast.values[1]);
821 if (ctx.error) return;
822 if (b.type != "NUMBER") return { type: "NUMBER" };
823 if (!a.value || !b.value) return { type: "NUMBER" };
824 return {
825 type: "NUMBER",
826 value: a.value.lesserOrEquals(b.value) ? bigInt(1) : bigInt(0)
827 };
828}
829
830function execGte(ctx, ast) {
831 const a = exec(ctx, ast.values[0]);
832 if (ctx.error) return;
833 if (a.type != "NUMBER") return { type: "NUMBER" };
834 const b = exec(ctx, ast.values[1]);
835 if (ctx.error) return;
836 if (b.type != "NUMBER") return { type: "NUMBER" };
837 if (!a.value || !b.value) return { type: "NUMBER" };
838 return {
839 type: "NUMBER",
840 value: a.value.greaterOrEquals(b.value) ? bigInt(1) : bigInt(0)
841 };
842}
843
844
845function execEq(ctx, ast) {
846 const a = exec(ctx, ast.values[0]);
847 if (ctx.error) return;
848 if (a.type != "NUMBER") return { type: "NUMBER" };
849 const b = exec(ctx, ast.values[1]);
850 if (ctx.error) return;
851 if (b.type != "NUMBER") return { type: "NUMBER" };
852 if (!a.value || !b.value) return { type: "NUMBER" };
853 return {
854 type: "NUMBER",
855 value: a.value.eq(b.value) ? bigInt(1) : bigInt(0)
856 };
857}
858
859function execNeq(ctx, ast) {
860 const a = exec(ctx, ast.values[0]);
861 if (ctx.error) return;
862 if (a.type != "NUMBER") return { type: "NUMBER" };
863 const b = exec(ctx, ast.values[1]);
864 if (ctx.error) return;
865 if (b.type != "NUMBER") return { type: "NUMBER" };
866 if (!a.value || !b.value) return { type: "NUMBER" };
867 return {
868 type: "NUMBER",
869 value: a.value.eq(b.value) ? bigInt(0) : bigInt(1)
870 };
871}
872
873function execBAnd(ctx, ast) {
874 const a = exec(ctx, ast.values[0]);
875 if (ctx.error) return;
876 if (a.type != "NUMBER") return { type: "NUMBER" };
877 const b = exec(ctx, ast.values[1]);
878 if (ctx.error) return;
879 if (b.type != "NUMBER") return { type: "NUMBER" };
880 if (!a.value || !b.value) return { type: "NUMBER" };
881 return {
882 type: "NUMBER",
883 value: a.value.and(b.value).and(__MASK__)
884 };
885}
886
887function execBOr(ctx, ast) {
888 const a = exec(ctx, ast.values[0]);
889 if (ctx.error) return;
890 if (a.type != "NUMBER") return { type: "NUMBER" };
891 const b = exec(ctx, ast.values[1]);
892 if (ctx.error) return;
893 if (b.type != "NUMBER") return { type: "NUMBER" };
894 if (!a.value || !b.value) return { type: "NUMBER" };
895 return {
896 type: "NUMBER",
897 value: a.value.or(b.value).and(__MASK__)
898 };
899}
900
901function execBXor(ctx, ast) {
902 const a = exec(ctx, ast.values[0]);
903 if (ctx.error) return;
904 if (a.type != "NUMBER") return { type: "NUMBER" };
905 const b = exec(ctx, ast.values[1]);
906 if (ctx.error) return;
907 if (b.type != "NUMBER") return { type: "NUMBER" };
908 if (!a.value || !b.value) return { type: "NUMBER" };
909 return {
910 type: "NUMBER",
911 value: a.value.xor(b.value).and(__MASK__)
912 };
913}
914
915function execBNot(ctx, ast) {
916 const a = exec(ctx, ast.values[0]);
917 if (ctx.error) return;
918 if (a.type != "NUMBER") return { type: "NUMBER" };
919 if (!a.value) return { type: "NUMBER" };
920
921 const res = lc.negate(a);
922 if (res.type == "ERROR") return error(ctx, ast, res.errStr);
923
924 return {
925 type: "NUMBER",
926 value: a.value.xor(__MASK__).and(__MASK__)
927 };
928}
929
930
931
932function execAnd(ctx, ast) {
933 const a = exec(ctx, ast.values[0]);
934 if (ctx.error) return;
935 if (a.type != "NUMBER") return { type: "NUMBER" };
936 const b = exec(ctx, ast.values[1]);
937 if (ctx.error) return;
938 if (b.type != "NUMBER") return { type: "NUMBER" };
939 if (!a.value || !b.value) return { type: "NUMBER" };
940 return {
941 type: "NUMBER",
942 value: (a.value.neq(0) && b.value.neq(0)) ? bigInt(1) : bigInt(0)
943 };
944}
945
946function execOr(ctx, ast) {
947 const a = exec(ctx, ast.values[0]);
948 if (ctx.error) return;
949 if (a.type != "NUMBER") return { type: "NUMBER" };
950 const b = exec(ctx, ast.values[1]);
951 if (ctx.error) return;
952 if (b.type != "NUMBER") return { type: "NUMBER" };
953 if (!a.value || !b.value) return { type: "NUMBER" };
954 return {
955 type: "NUMBER",
956 value: (a.value.neq(0) || b.value.neq(0)) ? bigInt(1) : bigInt(0)
957 };
958}
959
960function execLNot(ctx, ast) {
961 const a = exec(ctx, ast.values[0]);
962 if (ctx.error) return;
963 if (a.type != "NUMBER") return { type: "NUMBER" };
964 if (!a.value) return { type: "NUMBER" };
965 return {
966 type: "NUMBER",
967 value: (a.value.eq(0)) ? bigInt(1) : bigInt(0)
968 };
969}
970
971
972function execShl(ctx, ast) {
973 const a = exec(ctx, ast.values[0]);
974 if (ctx.error) return;
975 if (a.type != "NUMBER") return { type: "NUMBER" };
976 const b = exec(ctx, ast.values[1]);
977 if (ctx.error) return;
978 if (b.type != "NUMBER") return { type: "NUMBER" };
979 if (!a.value || !b.value) return { type: "NUMBER" };
980 const v = b.value.greater(256) ? 256 : b.value.value;
981 return {
982 type: "NUMBER",
983 value: a.value.shiftLeft(v).and(__MASK__)
984 };
985}
986
987function execShr(ctx, ast) {
988 const a = exec(ctx, ast.values[0]);
989 if (ctx.error) return;
990 if (a.type != "NUMBER") return { type: "NUMBER" };
991 const b = exec(ctx, ast.values[1]);
992 if (ctx.error) return;
993 if (b.type != "NUMBER") return { type: "NUMBER" };
994 if (!a.value || !b.value) return { type: "NUMBER" };
995 const v = b.value.greater(256) ? 256 : b.value.value;
996 return {
997 type: "NUMBER",
998 value: a.value.shiftRight(v).and(__MASK__)
999 };
1000}
1001
1002function execMod(ctx, ast) {
1003 const a = exec(ctx, ast.values[0]);
1004 if (ctx.error) return;
1005 if (a.type != "NUMBER") return { type: "NUMBER" };
1006 const b = exec(ctx, ast.values[1]);
1007 if (ctx.error) return;
1008 if (b.type != "NUMBER") return { type: "NUMBER" };
1009 if (!a.value || !b.value) return { type: "NUMBER" };
1010 return {
1011 type: "NUMBER",
1012 value: a.value.mod(b.value)
1013 };
1014}
1015
1016
1017function execExp(ctx, ast) {
1018 const a = exec(ctx, ast.values[0]);
1019 if (ctx.error) return;
1020 if (a.type != "NUMBER") return { type: "NUMBER" };
1021 const b = exec(ctx, ast.values[1]);
1022 if (ctx.error) return;
1023 if (b.type != "NUMBER") return { type: "NUMBER" };
1024 if (!a.value || !b.value) return { type: "NUMBER" };
1025 return {
1026 type: "NUMBER",
1027 value: a.value.modPow(b.value, __P__)
1028 };
1029}
1030
1031function execDiv(ctx, ast) {
1032 const a = exec(ctx, ast.values[0]);
1033 if (ctx.error) return;
1034 if (a.type != "NUMBER") return { type: "NUMBER" };
1035 const b = exec(ctx, ast.values[1]);
1036 if (ctx.error) return;
1037 if (b.type != "NUMBER") return { type: "NUMBER" };
1038 if (!a.value || !b.value) return { type: "NUMBER" };
1039 if (b.value.isZero()) return error(ctx, ast, "Division by zero");
1040 return {
1041 type: "NUMBER",
1042 value: a.value.times(b.value.modInv(__P__)).mod(__P__)
1043 };
1044}
1045
1046function execIDiv(ctx, ast) {
1047 const a = exec(ctx, ast.values[0]);
1048 if (ctx.error) return;
1049 if (a.type != "NUMBER") return { type: "NUMBER" };
1050 const b = exec(ctx, ast.values[1]);
1051 if (ctx.error) return;
1052 if (b.type != "NUMBER") return { type: "NUMBER" };
1053 if (!a.value || !b.value) return { type: "NUMBER" };
1054 if (b.value.isZero()) return error(ctx, ast, "Division by zero");
1055 return {
1056 type: "NUMBER",
1057 value: a.value.divide(b.value)
1058 };
1059}
1060
1061function execAdd(ctx, ast) {
1062 const a = exec(ctx, ast.values[0]);
1063 if (ctx.error) return;
1064 const b = exec(ctx, ast.values[1]);
1065 if (ctx.error) return;
1066
1067 const res = lc.add(a,b);
1068 if (res.type == "ERROR") return error(ctx, ast, res.errStr);
1069
1070 return res;
1071}
1072
1073function execSub(ctx, ast) {
1074 const a = exec(ctx, ast.values[0]);
1075 if (ctx.error) return;
1076 const b = exec(ctx, ast.values[1]);
1077 if (ctx.error) return;
1078
1079 const res = lc.sub(a,b);
1080 if (res.type == "ERROR") return error(ctx, ast, res.errStr);
1081
1082 return res;
1083}
1084
1085function execUMinus(ctx, ast) {
1086 const a = exec(ctx, ast.values[0]);
1087 if (ctx.error) return;
1088
1089 const res = lc.negate(a);
1090 if (res.type == "ERROR") return error(ctx, ast, res.errStr);
1091
1092 return res;
1093}
1094
1095function execMul(ctx, ast) {
1096 const a = exec(ctx, ast.values[0]);
1097 if (ctx.error) return;
1098 const b = exec(ctx, ast.values[1]);
1099 if (ctx.error) return;
1100
1101 const res = lc.mul(a,b);
1102 if (res.type == "ERROR") return error(ctx, ast, res.errStr);
1103
1104 return res;
1105}
1106
1107
1108function execVarAddAssignement(ctx, ast) {
1109 const res = execAdd(ctx,{ values: [ast.values[0], ast.values[1]] } );
1110 if (ctx.error) return;
1111 return execVarAssignement(ctx, { op:"=", values: [ast.values[0], res] });
1112}
1113
1114function execVarMulAssignement(ctx, ast) {
1115 const res = execMul(ctx,{ values: [ast.values[0], ast.values[1]] } );
1116 if (ctx.error) return;
1117 return execVarAssignement(ctx, { op:"=", values: [ast.values[0], res] });
1118}
1119
1120function execPlusPlusRight(ctx, ast) {
1121 const resBefore = exec(ctx, ast.values[0]);
1122 if (ctx.error) return;
1123 const resAfter = execAdd(ctx,{ values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}] } );
1124 if (ctx.error) return;
1125 execVarAssignement(ctx, { op:"=", values: [ast.values[0], resAfter] });
1126 return resBefore;
1127}
1128
1129function execPlusPlusLeft(ctx, ast) {
1130 if (ctx.error) return;
1131 const resAfter = execAdd(ctx,{ values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}] } );
1132 if (ctx.error) return;
1133 execVarAssignement(ctx, { op:"=", values: [ast.values[0], resAfter] });
1134 return resAfter;
1135}
1136
1137function execMinusMinusRight(ctx, ast) {
1138 const resBefore = exec(ctx, ast.values[0]);
1139 if (ctx.error) return;
1140 const resAfter = execSub(ctx,{ values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}] } );
1141 if (ctx.error) return;
1142 execVarAssignement(ctx, { op:"=", values: [ast.values[0], resAfter] });
1143 return resBefore;
1144}
1145
1146function execMinusMinusLeft(ctx, ast) {
1147 if (ctx.error) return;
1148 const resAfter = execSub(ctx,{ values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}] } );
1149 if (ctx.error) return;
1150 execVarAssignement(ctx, { op:"=", values: [ast.values[0], resAfter] });
1151 return resAfter;
1152}
1153
1154function execTerCon(ctx, ast) {
1155 const cond = exec(ctx, ast.values[0]);
1156 if (ctx.error) return;
1157
1158 if (!cond.value) return { type: "NUMBER" };
1159
1160 if (cond.value.neq(0)) {
1161 return exec(ctx, ast.values[1]);
1162 } else {
1163 return exec(ctx, ast.values[2]);
1164 }
1165}
1166
1167function execSignalAssign(ctx, ast) {
1168 let vDest;
1169 if (ast.values[0].type == "DECLARE") {
1170 vDest = exec(ctx, ast.values[0]);
1171 if (ctx.error) return;
1172 } else {
1173 vDest = ast.values[0];
1174 }
1175
1176 let dst;
1177 if (vDest.type == "VARIABLE") {
1178 dst = getScope(ctx, vDest.name, vDest.selectors);
1179 if (ctx.error) return;
1180 } else if (vDest.type == "PIN") {
1181 dst = execPin(ctx, vDest);
1182 if (ctx.error) return;
1183 } else {
1184 error(ctx, ast, "Bad assignement");
1185 }
1186
1187 if (!dst) return error(ctx, ast, "Signal not defined");
1188 if (dst.type != "SIGNAL") return error(ctx, ast, "Signal assigned to a non signal");
1189
1190 let sDest=ctx.signals[dst.sIdx];
1191 if (!sDest) return error(ctx, ast, "Invalid signal: "+dst.sIdx);
1192
1193 let isOut = (sDest.o & ctx.MAIN)&&(sDest.o & ctx.OUT);
1194 while (sDest.e>=0) {
1195 sDest=ctx.signals[sDest.e];
1196 isOut = isOut || ((sDest.o & ctx.MAIN)&&(sDest.o & ctx.OUT));
1197 }
1198
1199 if (sDest.value) return error(ctx, ast, "Signals cannot be assigned twice");
1200
1201 let src = exec(ctx, ast.values[1]);
1202 if (ctx.error) return;
1203
1204
1205 /*
1206 let vSrc;
1207 if (ast.values[1].type == "DECLARE") {
1208 vSrc = exec(ctx, ast.values[1]);
1209 if (ctx.error) return;
1210 } else {
1211 vSrc = ast.values[1];
1212 }
1213
1214 if (vSrc.type == "VARIABLE") {
1215 src = getScope(ctx, vSrc.name, vSrc.selectors);
1216 if (!src) error(ctx, ast, "Variable not defined: " + vSrc.name);
1217 if (ctx.error) return;
1218 } else if (vSrc.type == "PIN") {
1219 src = execPin(ctx, vSrc);
1220 }
1221 */
1222
1223 let assignValue = true;
1224 if (src.type == "SIGNAL") {
1225 let sSrc = ctx.signals[src.sIdx];
1226 let isIn = (sSrc.o & ctx.main)&&(sSrc.o & ctx.IN);
1227 while (sSrc.e>=0) {
1228 sSrc=ctx.signals[sSrc.e];
1229 isIn = isIn || ((sSrc.o & ctx.main)&&(sSrc.o & ctx.IN));
1230 }
1231
1232 // Skip if an out is assigned directly to an input.
1233 if ((!isIn)||(!isOut)) {
1234 sDest.e = src.sIdx;
1235 while (sDest.e >= 0) sDest=ctx.signals[sDest.e];
1236 assignValue = false;
1237 }
1238 }
1239
1240 if (assignValue) {
1241 // const resLC = exec(ctx, vSrc);
1242 if (ctx.error) return;
1243
1244 // const v = lc.evaluate(ctx, resLC);
1245 const v = lc.evaluate(ctx, src);
1246
1247 if (v.value) {
1248 sDest.v = v.value;
1249 }
1250 }
1251
1252 return vDest;
1253}
1254
1255function execConstrain(ctx, ast) {
1256 ast.fileName = ctx.fileName;
1257 ast.filePath = ctx.filePath;
1258 const a = exec(ctx, ast.values[0]);
1259 if (ctx.error) return;
1260 const b = exec(ctx, ast.values[1]);
1261 if (ctx.error) return;
1262
1263 const res = lc.sub(a,b);
1264 if (res.type == "ERROR") return error(ctx, ast, res.errStr);
1265
1266 if (!lc.isZero(res)) {
1267 ctx.constraints.push(lc.toQEQ(res));
1268 }
1269
1270 if ((ctx.constraints.length % 10000 == 0)&&(ctx.constraints.length>0)) console.log("Constraints: " + ctx.constraints.length);
1271
1272 return res;
1273}
1274
1275function execSignalAssignConstrain(ctx, ast) {
1276 const v = execSignalAssign(ctx,ast);
1277 if (ctx.error) return;
1278 execConstrain(ctx, ast);
1279 if (ctx.error) return;
1280 return v;
1281}
1282
1283function execInclude(ctx, ast) {
1284 const incFileName = path.resolve(ctx.filePath, ast.file);
1285 const incFilePath = path.dirname(incFileName);
1286
1287 ctx.includedFiles = ctx.includedFiles || [];
1288 if (ctx.includedFiles[incFileName]) return;
1289
1290
1291 const src = fs.readFileSync(incFileName, "utf8");
1292
1293 ctx.includedFiles[incFileName] = src.split("\n");
1294
1295 if (!src) return error(ctx, ast, "Include file not found: "+incFileName);
1296
1297 const incAst = parser.parse(src);
1298
1299 const oldFilePath = ctx.filePath;
1300 const oldFileName = ctx.fileName;
1301 ctx.filePath = incFilePath;
1302 ctx.fileName = incFileName;
1303
1304 exec(ctx, incAst);
1305
1306 ast.block = incAst;
1307
1308 ctx.filePath = oldFilePath;
1309 ctx.fileName = oldFileName;
1310}
1311
1312function execArray(ctx, ast) {
1313 const res = [];
1314
1315 for (let i=0; i<ast.values.length; i++) {
1316 res.push(exec(ctx, ast.values[i]));
1317 }
1318
1319 return res;
1320}
1321
1322function copyScope(scope) {
1323 var scopesClone = [];
1324 for (let i=0; i<scope.length; i++) {
1325 scopesClone.push(scope[i]);
1326 }
1327 return scopesClone;
1328}
1329