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