circom/src/gencode.js
src/gencode.jsRaw
1/*
2 Copyright 2018 0kims association
3
4 This file is part of jaz (Zero Knowlage Circuit compiler).
5
6 jaz is free software: you can redistribute it and/or modify
7 it 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 jaz is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with jaz. If not, see <https://www.gnu.org/licenses/>.
18*/
19
20const bigInt = require("big-integer");
21
22module.exports = gen;
23
24function ident(text) {
25 let lines = text.split("\n");
26 for (let i=0; i<lines.length; i++) {
27 if (lines[i]) lines[i] = " "+lines[i];
28 }
29 return lines.join("\n");
30}
31
32function gen(ctx, ast) {
33 if ((ast.type == "NUMBER") ) {
34 return genNumber(ctx, ast);
35 } else if (ast.type == "VARIABLE") {
36 return genVariable(ctx, ast);
37 } else if (ast.type == "PIN") {
38 return genPin(ctx, ast);
39 } else if (ast.type == "OP") {
40 if (ast.op == "=") {
41 return genVarAssignement(ctx, ast);
42 } else if (ast.op == "<--") {
43 return genVarAssignement(ctx, ast);
44 } else if (ast.op == "<==") {
45 return genSignalAssignConstrain(ctx, ast);
46 } else if (ast.op == "===") {
47 return genConstrain(ctx, ast);
48 } else if (ast.op == "+=") {
49 return genVarAddAssignement(ctx, ast);
50 } else if (ast.op == "*=") {
51 return genVarMulAssignement(ctx, ast);
52 } else if (ast.op == "+") {
53 return genAdd(ctx, ast);
54 } else if (ast.op == "-") {
55 return genSub(ctx, ast);
56 } else if (ast.op == "UMINUS") {
57 return genUMinus(ctx, ast);
58 } else if (ast.op == "*") {
59 return genMul(ctx, ast);
60 } else if (ast.op == "PLUSPLUSRIGHT") {
61 return genPlusPlusRight(ctx, ast);
62 } else if (ast.op == "PLUSPLUSLEFT") {
63 return genPlusPlusLeft(ctx, ast);
64 } else if (ast.op == "**") {
65 return genExp(ctx, ast);
66 } else if (ast.op == "&") {
67 return genBAnd(ctx, ast);
68 } else if (ast.op == "<<") {
69 return genShl(ctx, ast);
70 } else if (ast.op == ">>") {
71 return genShr(ctx, ast);
72 } else if (ast.op == "<") {
73 return genLt(ctx, ast);
74 } else if (ast.op == "==") {
75 return genEq(ctx, ast);
76 } else if (ast.op == "?") {
77 return genTerCon(ctx, ast);
78 } else {
79 error(ctx, ast, "Invalid operation: " + ast.op);
80 }
81 } else if (ast.type == "DECLARE") {
82 if (ast.declareType == "COMPONENT") {
83 return genDeclareComponent(ctx, ast);
84 } else if ((ast.declareType == "SIGNALIN")||
85 (ast.declareType == "SIGNALOUT")||
86 (ast.declareType == "SIGNAL")) {
87 return genDeclareSignal(ctx, ast);
88 } else if (ast.declareType == "VARIABLE") {
89 return genDeclareVariable(ctx, ast);
90 } else {
91 error(ctx, ast, "Invalid declaration: " + ast.declareType);
92 }
93 } else if (ast.type == "FUNCTIONCALL") {
94 return genFunctionCall(ctx, ast);
95 } else if (ast.type == "BLOCK") {
96 return genBlock(ctx, ast);
97 } else if (ast.type == "FOR") {
98 return genFor(ctx, ast);
99 } else if (ast.type == "WHILE") {
100 return genWhile(ctx, ast);
101 } else if (ast.type == "RETURN") {
102 return genReturn(ctx, ast);
103 } else if (ast.type == "TEMPLATEDEF") {
104 return genTemplateDef(ctx, ast);
105 } else if (ast.type == "FUNCTIONDEF") {
106 return genFunctionDef(ctx, ast);
107 } else if (ast.type == "INCLUDE") {
108 return genInclude(ctx, ast);
109 } else if (ast.type == "ARRAY") {
110 return genArray(ctx, ast);
111 } else {
112 error(ctx, ast, "GEN -> Invalid AST node type: " + ast.type);
113 }
114}
115
116
117function error(ctx, ast, errStr) {
118 ctx.error = {
119 pos: {
120 first_line: ast.first_line,
121 first_column: ast.first_column,
122 last_line: ast.last_line,
123 last_column: ast.last_column
124 },
125 errStr: errStr,
126 ast: ast
127 };
128}
129
130
131function getScope(ctx, name) {
132 for (let i=ctx.scopes.length-1; i>=0; i--) {
133 if (ctx.scopes[i][name]) return ctx.scopes[i][name];
134 }
135 return null;
136}
137
138
139function genFunctionCall(ctx, ast) {
140 let S = "[";
141 for (let i=0; i<ast.params.length; i++) {
142 if (i>0) S += ",";
143 S += gen(ctx, ast.params[i]);
144 }
145 S+="]";
146
147 return `ctx.callFunction("${ast.name}", ${S})`;
148}
149
150function genBlock(ctx, ast) {
151 let body = "";
152 for (let i=0; i<ast.statements.length; i++) {
153 const l = gen(ctx, ast.statements[i]);
154 if (ctx.error) return;
155 if (l) {
156 body += l;
157 if (body[body.length-1] != "\n") body += ";\n";
158 }
159 }
160 return "{\n"+ident(body)+"}\n";
161}
162
163function genTemplateDef(ctx, ast) {
164 let S = "function(ctx) ";
165
166 const newScope = {};
167 for (let i=0; i< ast.params.length; i++) {
168 newScope[ast.params[i]] = { type: "VARIABLE" };
169 }
170
171 ctx.scopes.push(newScope);
172 S += genBlock(ctx, ast.block);
173 ctx.scopes.pop();
174
175// const scope = ctx.scopes[ctx.scopes.length-1];
176 const scope = ctx.scopes[0]; // Scope for templates is top
177
178 scope[ast.name] = {
179 type: "TEMPLATE"
180 };
181
182 ctx.templates[ast.name] = S;
183 return "";
184}
185
186function genFunctionDef(ctx, ast) {
187 let S = "function(ctx) ";
188
189 const newScope = {};
190 const params = [];
191 for (let i=0; i< ast.params.length; i++) {
192 newScope[ast.params[i]] = { type: "VARIABLE" };
193 params.push(ast.params[i]);
194 }
195
196 ctx.scopes.push(newScope);
197 S += genBlock(ctx, ast.block);
198 ctx.scopes.pop();
199
200// const scope = ctx.scopes[0]; // Scope for templates is top
201 const scope = ctx.scopes[ctx.scopes.length-1];
202
203 scope[ast.name] = {
204 type: "FUNCTION"
205 };
206
207 ctx.functions[ast.name] = S;
208 ctx.functionParams[ast.name] = params;
209 return "";
210}
211
212function genFor(ctx, ast) {
213 const init = gen(ctx, ast.init);
214 if (ctx.error) return;
215 const condition = gen(ctx, ast.condition);
216 if (ctx.error) return;
217 const step = gen(ctx, ast.step);
218 if (ctx.error) return;
219 const body = gen(ctx, ast.body);
220 if (ctx.error) return;
221 return `for (${init};${condition};${step})\n${body}`;
222}
223
224function genWhile(ctx, ast) {
225 const condition = gen(ctx, ast.condition);
226 if (ctx.error) return;
227 const body = gen(ctx, ast.body);
228 if (ctx.error) return;
229 return `while (${condition})\n${body}`;
230}
231
232function genReturn(ctx, ast) {
233 const value = gen(ctx, ast.value);
234 if (ctx.error) return;
235 return `return ${value};`;
236}
237
238function genDeclareComponent(ctx, ast) {
239 const scope = ctx.scopes[ctx.scopes.length - 1];
240
241 if (ast.name.type != "VARIABLE") return error(ctx, ast, "Invalid component name");
242 if (getScope(ctx, ast.name.name)) return error(ctx, ast, "Name already exists: "+ast.name.name);
243
244 scope[ast.name.name] = {
245 type: "COMPONENT"
246 };
247
248 return "";
249}
250
251function genDeclareSignal(ctx, ast) {
252 const scope = ctx.scopes[ctx.scopes.length-1];
253
254 if (ast.name.type != "VARIABLE") return error(ctx, ast, "Invalid component name");
255 if (getScope(ctx, ast.name.name)) return error(ctx, ast, "Name already exists: "+ast.name.name);
256
257 scope[ast.name.name] = {
258 type: "SIGNAL"
259 };
260
261 return "";
262}
263
264function genDeclareVariable(ctx, ast) {
265 const scope = ctx.scopes[ctx.scopes.length-1];
266
267 if (ast.name.type != "VARIABLE") return error(ctx, ast, "Invalid component name");
268 if (getScope(ctx, ast.name.name)) return error(ctx, ast, "Name already exists: "+ast.name.name);
269
270 scope[ast.name.name] = {
271 type: "VARIABLE"
272 };
273
274 return "";
275}
276
277function genNumber(ctx, ast) {
278 return `"${ast.value.toString()}"`;
279}
280
281function genVariable(ctx, ast) {
282 const v = getScope(ctx, ast.name);
283
284 const sels = [];
285 for (let i=0; i<ast.selectors.length; i++) {
286 sels.push(gen(ctx, ast.selectors[i]));
287 if (ctx.error) return;
288 }
289
290
291 if (v.type == "VARIABLE") {
292 return `ctx.getVar("${ast.name}",[${sels.join(",")}])`;
293 } else if (v.type == "SIGNAL") {
294 return `ctx.getSignal("${ast.name}", [${sels.join(",")}])`;
295 } else {
296 error(ctx, ast, "Invalid Variable type");
297 }
298}
299
300function genPin(ctx, ast) {
301 let componentName = ast.component.name;
302 let componentSelectors = [];
303 for (let i=0; i<ast.component.selectors.length; i++) {
304 componentSelectors.push(gen(ctx, ast.component.selectors[i]));
305 }
306 componentSelectors = "["+ componentSelectors.join(",") + "]";
307 let pinName = ast.pin.name;
308 let pinSelectors = [];
309 for (let i=0; i<ast.pin.selectors.length; i++) {
310 pinSelectors.push(gen(ctx, ast.pin.selectors[i]));
311 }
312 pinSelectors = "["+ pinSelectors.join(",") + "]";
313 return `ctx.getPin("${componentName}", ${componentSelectors}, "${pinName}", ${pinSelectors})`;
314}
315
316function genVarAssignement(ctx, ast) {
317
318 const sels = [];
319 let vName;
320
321 if (ctx.error) return;
322
323 if (ast.values[0].type == "PIN") {
324 let componentName = ast.values[0].component.name;
325 let componentSelectors = [];
326 for (let i=0; i<ast.values[0].component.selectors.length; i++) {
327 componentSelectors.push(gen(ctx, ast.values[0].component.selectors[i]));
328 }
329 componentSelectors = "["+ componentSelectors.join(",") + "]";
330 let pinName = ast.values[0].pin.name;
331 let pinSelectors = [];
332 for (let i=0; i<ast.values[0].pin.selectors.length; i++) {
333 pinSelectors.push(gen(ctx, ast.values[0].pin.selectors[i]));
334 }
335 pinSelectors = "["+ pinSelectors.join(",") + "]";
336 const res = gen(ctx, ast.values[1]);
337 return `ctx.setPin("${componentName}", ${componentSelectors}, "${pinName}", ${pinSelectors}, ${res})`;
338 }
339
340 if (ast.values[0].type == "DECLARE") {
341 gen(ctx, ast.values[0]);
342 if (ctx.error) return;
343 vName = ast.values[0].name.name;
344 } else {
345 vName = ast.values[0].name;
346 for (let i=0; i<ast.values[0].selectors.length; i++) {
347 sels.push(gen(ctx, ast.values[0].selectors[i]));
348 if (ctx.error) return;
349 }
350 }
351
352
353 const v = getScope(ctx, vName);
354
355 // Component instantiation is already done.
356 if (v.type == "COMPONENT") return "";
357
358 const res = gen(ctx, ast.values[1]);
359 if (v.type == "VARIABLE") {
360 return `ctx.setVar("${vName}", [${sels.join(",")}], ${res})`;
361 } else if (v.type == "SIGNAL") {
362 return `ctx.setSignal("${vName}", [${sels.join(",")}], ${res})`;
363 } else {
364 return error(ctx, ast, "Assigning to invalid");
365 }
366}
367
368function genConstrain(ctx, ast) {
369 const a = gen(ctx, ast.values[0]);
370 if (ctx.error) return;
371 const b = gen(ctx, ast.values[1]);
372 if (ctx.error) return;
373 return `ctx.assert(${a}, ${b})`;
374}
375
376function genSignalAssignConstrain(ctx, ast) {
377 return genVarAssignement(ctx, ast) + ";\n" + genConstrain(ctx, ast);
378}
379
380function genVarAddAssignement(ctx, ast) {
381 return genVarAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "+", values: ast.values}]});
382}
383
384function genVarMulAssignement(ctx, ast) {
385 return genVarAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "*", values: ast.values}]});
386}
387
388function genPlusPlusRight(ctx, ast) {
389 return `(${genVarAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "+", values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}]}]})}).add(__P__).sub(bigInt(1)).mod(__P__)`;
390}
391
392function genPlusPlusLeft(ctx, ast) {
393 return genVarAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "+", values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}]}]});
394}
395
396function genAdd(ctx, ast) {
397 const a = gen(ctx, ast.values[0]);
398 if (ctx.error) return;
399 const b = gen(ctx, ast.values[1]);
400 if (ctx.error) return;
401 return `bigInt(${a}).add(bigInt(${b})).mod(__P__)`;
402}
403
404function genMul(ctx, ast) {
405 const a = gen(ctx, ast.values[0]);
406 if (ctx.error) return;
407 const b = gen(ctx, ast.values[1]);
408 if (ctx.error) return;
409 return `bigInt(${a}).mul(bigInt(${b})).mod(__P__)`;
410}
411
412function genSub(ctx, ast) {
413 const a = gen(ctx, ast.values[0]);
414 if (ctx.error) return;
415 const b = gen(ctx, ast.values[1]);
416 if (ctx.error) return;
417 return `bigInt(${a}).add(__P__).sub(bigInt(${b})).mod(__P__)`;
418}
419
420function genExp(ctx, ast) {
421 const a = gen(ctx, ast.values[0]);
422 if (ctx.error) return;
423 const b = gen(ctx, ast.values[1]);
424 if (ctx.error) return;
425 return `bigInt(${a}).modPow(bigInt(${b}), __P__)`;
426}
427
428function genBAnd(ctx, ast) {
429 const a = gen(ctx, ast.values[0]);
430 if (ctx.error) return;
431 const b = gen(ctx, ast.values[1]);
432 if (ctx.error) return;
433 return `bigInt(${a}).and(bigInt(${b})).and(__MASK__)`;
434}
435
436function genShl(ctx, ast) {
437 const a = gen(ctx, ast.values[0]);
438 if (ctx.error) return;
439 const b = gen(ctx, ast.values[1]);
440 if (ctx.error) return;
441 return `bigInt(${b}).greater(bigInt(256)) ? 0 : bigInt(${a}).shl(bigInt(${b})).and(__MASK__)`;
442}
443
444function genShr(ctx, ast) {
445 const a = gen(ctx, ast.values[0]);
446 if (ctx.error) return;
447 const b = gen(ctx, ast.values[1]);
448 if (ctx.error) return;
449 return `bigInt(${b}).greater(bigInt(256)) ? 0 : bigInt(${a}).shr(bigInt(${b})).and(__MASK__)`;
450}
451
452function genLt(ctx, ast) {
453 const a = gen(ctx, ast.values[0]);
454 if (ctx.error) return;
455 const b = gen(ctx, ast.values[1]);
456 if (ctx.error) return;
457 return `bigInt(${a}).lt(bigInt(${b})) ? 1 : 0`;
458}
459
460function genEq(ctx, ast) {
461 const a = gen(ctx, ast.values[0]);
462 if (ctx.error) return;
463 const b = gen(ctx, ast.values[1]);
464 if (ctx.error) return;
465 return `bigInt(${a}).eq(bigInt(${b})) ? 1 : 0`;
466}
467
468function genUMinus(ctx, ast) {
469 const a = gen(ctx, ast.values[0]);
470 if (ctx.error) return;
471 return `__P__.sub(bigInt(${a})).mod(__P__)`;
472}
473
474function genTerCon(ctx, ast) {
475 const a = gen(ctx, ast.values[0]);
476 if (ctx.error) return;
477 const b = gen(ctx, ast.values[1]);
478 if (ctx.error) return;
479 const c = gen(ctx, ast.values[2]);
480 if (ctx.error) return;
481 return `bigInt(${a}).neq(bigInt(0)) ? (${b}) : (${c})`;
482}
483
484function genInclude(ctx, ast) {
485 return ast.block ? gen(ctx, ast.block) : "";
486}
487
488function genArray(ctx, ast) {
489 let S = "[";
490 for (let i=0; i<ast.values.length; i++) {
491 if (i>0) S += ",";
492 S += gen(ctx, ast.values[i]);
493 }
494 S+="]";
495 return S;
496}
497