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