/* Copyright 2018 0KIMS association. This file is part of circom (Zero Knowledge Circuit Compiler). circom is a free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. circom is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with circom. If not, see . */ const path = require("path"); const fs = require("fs"); const bigInt = require("big-integer"); const __P__ = new bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617"); const __MASK__ = new bigInt(2).pow(253).minus(1); const lc = require("./lcalgebra"); const parser = require("../parser/jaz.js").parser; /* TODO: Add lines information function setLines(dst, first, last) { last = last || first; dst.first_line = first.first_line; dst.first_column = first.first_column; dst.last_line = last.last_line; dst.last_column = last.last_column; } */ module.exports = exec; function exec(ctx, ast) { if (!ast) { return error(ctx, ast, "Null AST"); } if ((ast.type == "NUMBER") || (ast.type == "LINEARCOMBINATION") || (ast.type =="SIGNAL") || (ast.type == "QEQ")) { return ast; } else if (ast.type == "VARIABLE") { return execVariable(ctx, ast); } else if (ast.type == "PIN") { return execPin(ctx, ast); } else if (ast.type == "OP") { if (ast.op == "=") { return execVarAssignement(ctx, ast); } else if (ast.op == "<--") { return execSignalAssign(ctx, ast); } else if (ast.op == "<==") { return execSignalAssignConstrain(ctx, ast); } else if (ast.op == "===") { return execConstrain(ctx, ast); } else if (ast.op == "+=") { return execVarAddAssignement(ctx, ast); } else if (ast.op == "*=") { return execVarMulAssignement(ctx, ast); } else if (ast.op == "+") { return execAdd(ctx, ast); } else if (ast.op == "-") { return execSub(ctx, ast); } else if (ast.op == "UMINUS") { return execUMinus(ctx, ast); } else if (ast.op == "*") { return execMul(ctx, ast); } else if (ast.op == "%") { return execMod(ctx, ast); } else if (ast.op == "PLUSPLUSRIGHT") { return execPlusPlusRight(ctx, ast); } else if (ast.op == "PLUSPLUSLEFT") { return execPlusPlusLeft(ctx, ast); } else if (ast.op == "MINUSMINUSRIGHT") { return execMinusMinusRight(ctx, ast); } else if (ast.op == "MINUSMINUSLEFT") { return execMinusMinusLeft(ctx, ast); } else if (ast.op == "/") { return execDiv(ctx, ast); } else if (ast.op == "\\") { return execIDiv(ctx, ast); } else if (ast.op == "**") { return execExp(ctx, ast); } else if (ast.op == "&") { return execBAnd(ctx, ast); } else if (ast.op == "&&") { return execAnd(ctx, ast); } else if (ast.op == "||") { return execOr(ctx, ast); } else if (ast.op == "<<") { return execShl(ctx, ast); } else if (ast.op == ">>") { return execShr(ctx, ast); } else if (ast.op == "<") { return execLt(ctx, ast); } else if (ast.op == ">") { return execGt(ctx, ast); } else if (ast.op == "<=") { return execLte(ctx, ast); } else if (ast.op == ">=") { return execGte(ctx, ast); } else if (ast.op == "==") { return execEq(ctx, ast); } else if (ast.op == "!=") { return execNeq(ctx, ast); } else if (ast.op == "?") { return execTerCon(ctx, ast); } else { error(ctx, ast, "Invalid operation: " + ast.op); } } else if (ast.type == "DECLARE") { if (ast.declareType == "COMPONENT") { return execDeclareComponent(ctx, ast); } else if ((ast.declareType == "SIGNALIN")|| (ast.declareType == "SIGNALOUT")|| (ast.declareType == "SIGNAL")) { return execDeclareSignal(ctx, ast); } else if (ast.declareType == "VARIABLE") { return execDeclareVariable(ctx, ast); } else { error(ctx, ast, "Invalid declaration: " + ast.declareType); } } else if (ast.type == "FUNCTIONCALL") { return execFunctionCall(ctx, ast); } else if (ast.type == "BLOCK") { return execBlock(ctx, ast); } else if (ast.type == "COMPUTE") { return ; } else if (ast.type == "FOR") { return execFor(ctx, ast); } else if (ast.type == "WHILE") { return execWhile(ctx, ast); } else if (ast.type == "IF") { return execIf(ctx, ast); } else if (ast.type == "RETURN") { return execReturn(ctx, ast); } else if (ast.type == "TEMPLATEDEF") { return execTemplateDef(ctx, ast); } else if (ast.type == "FUNCTIONDEF") { return execFunctionDef(ctx, ast); } else if (ast.type == "INCLUDE") { return execInclude(ctx, ast); } else if (ast.type == "ARRAY") { return execArray(ctx, ast); } else { error(ctx, ast, "Invalid AST node type: " + ast.type); } } function error(ctx, ast, errStr) { ctx.error = { pos: { first_line: ast.first_line, first_column: ast.first_column, last_line: ast.last_line, last_column: ast.last_column }, errStr: errStr, errFile: ctx.fileName, ast: ast, message: errStr }; } function iterateSelectors(ctx, sizes, baseName, fn) { if (sizes.length == 0) { return fn(baseName); } const res = []; for (let i=0; i=0; i--) { if (ctx.scopes[i][name]) return select(ctx.scopes[i][name], sels); } return null; function reduce(v, _sels, idxName) { let sels = _sels || []; let sizes = v.sizes || []; let accSizes = [1]; for (let i=sizes.length-1; i>0; i--) { accSizes = [accSizes[0]*sizes[i], ...accSizes]; } const res = Object.assign({}, v); res.sizes = sizes.slice(sels.length); for (let i=0; i=0; i--) { if (ctx.scopes[i][name]) return i; } return -1; } function execBlock(ctx, ast) { for (let i=0; i=0) { sDest=ctx.signals[sDest.e]; isOut = isOut || ((sDest.o & ctx.MAIN)&&(sDest.o & ctx.OUT)); } if (sDest.value) return error(ctx, ast, "Signals cannot be assigned twice"); let src = exec(ctx, ast.values[1]); if (ctx.error) return; /* let vSrc; if (ast.values[1].type == "DECLARE") { vSrc = exec(ctx, ast.values[1]); if (ctx.error) return; } else { vSrc = ast.values[1]; } if (vSrc.type == "VARIABLE") { src = getScope(ctx, vSrc.name, vSrc.selectors); if (!src) error(ctx, ast, "Variable not defined: " + vSrc.name); if (ctx.error) return; } else if (vSrc.type == "PIN") { src = execPin(ctx, vSrc); } */ let assignValue = true; if (src.type == "SIGNAL") { let sSrc = ctx.signals[src.sIdx]; let isIn = (sSrc.o & ctx.main)&&(sSrc.o & ctx.IN); while (sSrc.e>=0) { sSrc=ctx.signals[sSrc.e]; isIn = isIn || ((sSrc.o & ctx.main)&&(sSrc.o & ctx.IN)); } // Skip if an out is assigned directly to an input. if ((!isIn)||(!isOut)) { sDest.e = src.sIdx; while (sDest.e >= 0) sDest=ctx.signals[sDest.e]; assignValue = false; } } if (assignValue) { // const resLC = exec(ctx, vSrc); if (ctx.error) return; // const v = lc.evaluate(ctx, resLC); const v = lc.evaluate(ctx, src); if (v.value) { sDest.value = v.value; } } return vDest; } function execConstrain(ctx, ast) { ast.fileName = ctx.fileName; ast.filePath = ctx.filePath; const a = exec(ctx, ast.values[0]); if (ctx.error) return; const b = exec(ctx, ast.values[1]); if (ctx.error) return; const res = lc.sub(a,b); if (res.type == "ERROR") return error(ctx, ast, res.errStr); if (!lc.isZero(res)) { ctx.constraints.push(lc.toQEQ(res)); } if ((ctx.constraints.length % 10000 == 0)&&(ctx.constraints.length>0)) console.log("Constraints: " + ctx.constraints.length); return res; } function execSignalAssignConstrain(ctx, ast) { const v = execSignalAssign(ctx,ast); if (ctx.error) return; execConstrain(ctx, ast); if (ctx.error) return; return v; } function execInclude(ctx, ast) { const incFileName = path.resolve(ctx.filePath, ast.file); const incFilePath = path.dirname(incFileName); ctx.includedFiles = ctx.includedFiles || []; if (ctx.includedFiles[incFileName]) return; const src = fs.readFileSync(incFileName, "utf8"); ctx.includedFiles[incFileName] = src.split("\n"); if (!src) return error(ctx, ast, "Include file not found: "+incFileName); const incAst = parser.parse(src); const oldFilePath = ctx.filePath; const oldFileName = ctx.fileName; ctx.filePath = incFilePath; ctx.fileName = incFileName; exec(ctx, incAst); ast.block = incAst; ctx.filePath = oldFilePath; ctx.fileName = oldFileName; } function execArray(ctx, ast) { const res = []; for (let i=0; i