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