const bigInt = require("big-integer"); const utils = require("./utils"); const assert = require("assert"); module.exports.gen = gen; module.exports.newRef = newRef; function newRef(ctx, type, _name, value, _sizes) { const isValue = ((typeof(value) != "undefined")&&(value != null)); let name; let sizes; if (!_name) { name = ctx.getTmpName(); } else { if (_name[0] =="_") { name = ctx.getTmpName(_name); } else { name = _name; } } if (typeof(_sizes) == "string") { sizes = _sizes; } else if (Array.isArray(_sizes)) { sizes = newSizes(ctx, _sizes); } else if (isValue) { sizes = newSizes(ctx, utils.extractSizes(value)); } else { sizes = newSizes(ctx, []); } const scope = ctx.scopes[ctx.scopes.length-1]; if (scope[name]) return error(ctx, null, "Variable already exists: " + name); const label = scope._prefix + name; scope[name] = { stack: true, type: type, used: false, sizes: sizes, label: label }; if (isValue) { scope[name].value = value; } return name; } function newSizes(ctx, sizes) { const scope = ctx.scopes[ctx.scopes.length-1]; const name = ctx.getTmpName("_sz"); scope[name] = { stack: true, type: "SIZES", used: false, dim: sizes.length, label: name, value: sizes }; return name; } function instantiateRef(ctx, name, initValue) { const v = getScope(ctx, name); if (!v.stack) return error(ctx, null, "Using a non existing var: " + name); if (v.used) return; if (v.type=="BIGINT") { const iSize = getScope(ctx, v.sizes); if (iSize.used) { const labelSize = iSize.label; ctx.codeHeader += `PBigInt ${v.label};\n`; const c = `${v.label} = ctx->allocBigInts(${labelSize});\n`; if (ctx.conditionalCode) { ctx.conditionalCodeHeader += c; } else { ctx.code += c; } ctx.codeFooter += `ctx->freeBigInts(${v.label}, ${labelSize});\n`; } else if (iSize.value) { const labelSize = ctx.addSizes(iSize.value); ctx.codeHeader += `PBigInt ${v.label} = ctx->allocBigInts(${labelSize});\n`; ctx.codeFooter += `ctx->freeBigInts(${v.label}, ${labelSize});\n`; } else { return error(ctx, null, "Undefined Sizes: " +name); } } else if (v.type=="INT") { ctx.codeHeader += `int ${v.label};\n`; } else if (v.type=="SIZES") { ctx.codeHeader += `Circom_Sizes ${v.label};\n`; } v.used = true; if ((typeof initValue!= "undefined")&&(initValue != null)) { const flatedValue = utils.flatArray(initValue); for (let i=0; iallocBigInts(${labelSize});\n`; for (let i=0; ifreeBigInts(${res}, ${labelSize});\n`; return res; } 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, ast: ast, message: errStr }; } function getScope(ctx, name) { for (let i=ctx.scopes.length-1; i>=0; i--) { if (ctx.scopes[i][name]) return ctx.scopes[i][name]; } return null; } function gen(ctx, ast) { if ((ast.type == "NUMBER") ) { return genNumber(ctx, ast); } else if (ast.type == "VARIABLE") { return genVariable(ctx, ast); } else if (ast.type == "PIN") { return genPin(ctx, ast); } else if (ast.type == "OP") { if (ast.op == "=") { return genVarAssignement(ctx, ast); } else if (ast.op == "<--") { return genVarAssignement(ctx, ast); } else if (ast.op == "<==") { return genSignalAssignConstrain(ctx, ast); } else if (ast.op == "===") { return genConstraint(ctx, ast); } else if (ast.op == "+=") { return genVarAddAssignement(ctx, ast); } else if (ast.op == "*=") { return genVarMulAssignement(ctx, ast); } else if (ast.op == "+") { return genBinaryOp(ctx, ast, "add"); } else if (ast.op == "-") { return genSub(ctx, ast); } else if (ast.op == "UMINUS") { return genUMinus(ctx, ast); } else if (ast.op == "*") { return genMul(ctx, ast); } else if (ast.op == "%") { return genMod(ctx, ast); } else if (ast.op == "PLUSPLUSRIGHT") { return genPlusPlusRight(ctx, ast); } else if (ast.op == "PLUSPLUSLEFT") { return genPlusPlusLeft(ctx, ast); } else if (ast.op == "MINUSMINUSRIGHT") { return genMinusMinusRight(ctx, ast); } else if (ast.op == "MINUSMINUSLEFT") { return genMinusMinusLeft(ctx, ast); } else if (ast.op == "**") { return genExp(ctx, ast); } else if (ast.op == "/") { return genDiv(ctx, ast); } else if (ast.op == "\\") { return genIDiv(ctx, ast); } else if (ast.op == "&") { return genBAnd(ctx, ast); } else if (ast.op == "&&") { return genAnd(ctx, ast); } else if (ast.op == "||") { return genOr(ctx, ast); } else if (ast.op == "<<") { return genShl(ctx, ast); } else if (ast.op == ">>") { return genShr(ctx, ast); } else if (ast.op == "<") { return genBinaryOp(ctx, ast, "lt"); } else if (ast.op == ">") { return genGt(ctx, ast); } else if (ast.op == "<=") { return genLte(ctx, ast); } else if (ast.op == ">=") { return genGte(ctx, ast); } else if (ast.op == "==") { return genEq(ctx, ast); } else if (ast.op == "!=") { return genNeq(ctx, ast); } else if (ast.op == "?") { return genTerCon(ctx, ast); } else { error(ctx, ast, "Invalid operation: " + ast.op); } } else if (ast.type == "DECLARE") { if (ast.declareType == "COMPONENT") { return genDeclareComponent(ctx, ast); } else if ((ast.declareType == "SIGNALIN")|| (ast.declareType == "SIGNALOUT")|| (ast.declareType == "SIGNAL")) { return genDeclareSignal(ctx, ast); } else if (ast.declareType == "VARIABLE") { return genDeclareVariable(ctx, ast); } else { error(ctx, ast, "Invalid declaration: " + ast.declareType); } } else if (ast.type == "FUNCTIONCALL") { return genFunctionCall(ctx, ast); } else if (ast.type == "BLOCK") { return genBlock(ctx, ast); } else if (ast.type == "COMPUTE") { return genCompute(ctx, ast); } else if (ast.type == "FOR") { return genFor(ctx, ast); } else if (ast.type == "WHILE") { return genWhile(ctx, ast); } else if (ast.type == "IF") { return genIf(ctx, ast); } else if (ast.type == "RETURN") { return genReturn(ctx, ast); } else if (ast.type == "INCLUDE") { return genInclude(ctx, ast); } else if (ast.type == "ARRAY") { return genArray(ctx, ast); } else { error(ctx, ast, "GEN -> Invalid AST node type: " + ast.type); } } function genBlock(ctx, ast) { ctx.scopes.push({_prefix : ""}); const oldCode = ctx.code; let res = null; ctx.code = ""; for (let i=0; i=0 ? p2 : undefined)); } return codes.join("\n"); } function genSrcComment(ctx, ast) { const code = getSource(ctx, ast); ctx.code += "\n/* "+code+" */\n"; } function genForSrcComment(ctx, ast) { const init = getSource(ctx, ast.init); const condition = getSource(ctx, ast.condition); const step = getSource(ctx, ast.step); ctx.code += `\n/* for (${init},${condition},${step}) */\n`; } function genDeclareComponent(ctx, ast) { const scope = ctx.scopes[ctx.scopes.length - 1]; if (ast.name.type != "VARIABLE") return error(ctx, ast, "Invalid component name"); if (getScope(ctx, ast.name.name)) return error(ctx, ast, "Name already exists: "+ast.name.name); scope[ast.name.name] = { type: "COMPONENT", label: ast.name.name }; return ast.name.name; } function genDeclareSignal(ctx, ast) { const scope = ctx.scopes[ctx.scopes.length-1]; if (ast.name.type != "VARIABLE") return error(ctx, ast, "Invalid component name"); if (getScope(ctx, ast.name.name)) return error(ctx, ast, "Name already exists: "+ast.name.name); const res = { type: "SIGNAL", label: ast.name.name }; scope[ast.name.name] = res; return res; } function genDeclareVariable(ctx, ast) { const scope = ctx.scopes[ctx.scopes.length-1]; const varName = ast.name.name; const labelName = scope._prefix + ast.name.name; if (ast.name.type != "VARIABLE") return error(ctx, ast, "Invalid component name"); if (typeof scope[varName] != "undefined") return error(ctx, ast, "Name already exists: "+varName); const sizes=[]; let instantiate = false; for (let i=0; i< ast.name.selectors.length; i++) { const size = gen(ctx, ast.name.selectors[i]); if (ctx.error) return; if (size.used) { instantiate = true; sizes.push(`BigInt2Int(${scope[size].label})`); } else { sizes.push(size.value.toJSNumber()); } sizes.push( size.value.toJSNumber() ); } const vSizes = newRef(ctx, "SIZES", "_sizes"); const iSizes = scope[vSizes]; iSizes.dim = ast.name.selectors.length; if (instantiate) { instantiateRef(ctx, vSizes); ctx.code += `${iSizes.label}[${iSizes.dim+1}]=0`; ctx.code += `${iSizes.label}[${iSizes.dim}]=1`; for (let i=iSizes.dim-1; i>=0; i--) { ctx.code += `${iSizes.label}[${i}] = ${sizes[i]}*${iSizes.label}[${i+1}];\n`; } } else { iSizes.value = sizes; } const res = scope[varName] = { stack: true, type: "BIGINT", sizes: vSizes, label: labelName, used: false, }; scope[varName] = res; return varName; } function genNumber(ctx, ast) { return newRef(ctx, "BIGINT", "_num", ast.value); } function genGetOffset(ctx, vOffset, vSizes, sels) { let rN = 0; let rStr = ""; let iOffset; if (vOffset) { iOffset = getScope(ctx, vOffset); if (iOffset.used) { rStr += iOffset.label; } else { rN += iOffset.value.toJSNumber(); } } if ((sels)&&(sels.length>0)) { const iSizes = getScope(ctx, vSizes); for (let i=0; i0) { if (rStr != "") rStr += " + "; rStr += rN; rN =0; } if (rStr != "") rStr += " + "; if (iIdx.used) { rStr += vIdx; } else { rStr += iIdx.value; } rStr += "*"; if (iSizes.used) { rStr += `${iSizes.label}[${i+1}]`; } else { rStr += iSizes.sizes[i+1]; } } } } if (rStr == "") { const o = newRef(ctx, "INT", "_offset", rN); return o; } else { if (rN>0) { if (rStr != "") rStr += " + "; rStr += rN; rN =0; } if (rStr == iOffset.label) { return vOffset; } else { const res = newRef(ctx, "INT", "_offset"); instantiateRef(ctx, res); ctx.code += `${res} = ${rStr};\n`; return res; } } } function genVariable(ctx, ast) { const v = getScope(ctx, ast.name); if (v.type == "SIGNAL") { let vOffset; if (ast.selectors.length>0) { const vsOffset = genGetSigalOffset(ctx, "ctx->cIdx", ast.name); const vsSizes = genGetSignalSizes(ctx, "ctx->cIdx", ast.name); vOffset = genGetOffset(ctx, vsOffset, vsSizes, ast.selectors ); } else { vOffset = genGetSigalOffset(ctx, "ctx->cIdx", ast.name); } return genGetSignal(ctx, "ctx->cIdx", vOffset); } else if (v.type == "BIGINT") { const vOffset = genGetOffset(ctx, 0, v.sizes, ast.sels ); const offset = getScope(ctx, vOffset); if (v.used) { if (offset.used) { const res = newRef(ctx, "BIGINT", "_v"); instantiateRef(ctx, res); ctx.code += `${res} = ${ast.name} + ${vOffset};\n`; return res; } else if (offset.value) { const res = newRef(ctx, "BIGINT", "_v"); instantiateRef(ctx, res); ctx.code += `${res} = ${ast.name} + ${vOffset.value};\n`; return res; } else { return ast.name; } } else { if (offset.used) { instantiateRef(ctx, ast.name); const vConstant = instantiateConstant(ctx, v.value); const res = newRef(ctx, "BIGINT", "_v"); instantiateRef(ctx, res); ctx.code += `${res} = ${vConstant} + ${vOffset};\n`; return res; } else { const sa = utils.subArray(v.value, ast.selectors); const res = newRef(ctx, "BIGINT", "_v", sa); return res; } } } const sels = []; for (let i=0; i0) { const vcOffset = genGetSubComponentOffset(ctx, "ctx->cIdx", ast.component.name); const vcSizes = genGetSubComponentSizes(ctx, "ctx->cIdx", ast.component.name); vcIdx = genGetOffset(ctx, vcOffset, vcSizes, ast.component.selectors ); } else { vcIdx = genGetSubComponentOffset(ctx, "ctx->cIdx", ast.component.name); } let vsIdx; if (ast.pin.selectors.length>0) { const vsOffset = genGetSigalOffset(ctx, vcIdx, ast.pin.name); const vsSizes = genGetSignalSizes(ctx, vcIdx, ast.pin.name); vsIdx = genGetOffset(ctx, vsOffset, vsSizes, ast.pin.selectors ); } else { vsIdx = genGetSigalOffset(ctx, vcIdx, ast.pin.name); } return genGetSignal(ctx, vcIdx, vsIdx); } function genGetSubComponentOffset(ctx, cIdx, label) { const vOffset = newRef(ctx, "INT", "_compIdx"); instantiateRef(ctx, vOffset); const h = utils.fnvHash(label); ctx.code += `${vOffset} = ctx->getSubComponentOffset(${cIdx}, 0x${h}LL /* ${label} */);\n`; return vOffset; } function genGetSubComponentSizes(ctx, cIdx, label) { const vSizes = newRef(ctx, "SIZES", "_compSizes"); instantiateRef(ctx, vSizes); const h = utils.fnvHash(label); ctx.code += `${vSizes} = ctx->getSubComponentSizes(${cIdx}, 0x${h}LL /* ${label} */);\n`; return vSizes; } function genGetSigalOffset(ctx, sIdx, label) { const vOffset = newRef(ctx, "INT", "_sigIdx"); instantiateRef(ctx, vOffset); const h = utils.fnvHash(label); ctx.code += `${vOffset} = ctx->getSignalOffset(${sIdx}, 0x${h}LL /* ${label} */);\n`; return vOffset; } function genGetSignalSizes(ctx, sIdx, label) { const vSizes = newRef(ctx, "SIZES", "_sigSizes"); instantiateRef(ctx, vSizes); const h = utils.fnvHash(label); ctx.code += `${vSizes} = ctx->getSignalSizes(${sIdx}, 0x${h}LL /* ${label} */);\n`; return vSizes; } function genSetSignal(ctx, cIdx, sIdx, value) { const v = getScope(ctx, value); if (!v.used) { value = instantiateConstant(ctx, v.value); } ctx.code += `ctx->setSignal(${cIdx}, ${sIdx}, ${value});\n`; return value; } function genGetSignal(ctx, cIdx, sIdx) { const res = newRef(ctx, "BIGINT", "_sigValue"); instantiateRef(ctx, res); ctx.code += `ctx->getSignal(${cIdx}, ${sIdx}, ${res});\n`; return res; } function genVarAssignement(ctx, ast) { let lName; let sels; if (ctx.error) return; if (ast.values[0].type == "PIN") { let vcIdx; if (ast.values[0].component.selectors.length>0) { const vcOffset = genGetSubComponentOffset(ctx, "ctx->cIdx", ast.values[0].component.name); const vcSizes = genGetSubComponentSizes(ctx, "ctx->cIdx", ast.values[0].component.name); vcIdx = genGetOffset(ctx, vcOffset, vcSizes, ast.values[0].component.selectors ); } else { vcIdx = genGetSubComponentOffset(ctx, "ctx->cIdx", ast.values[0].component.name); } let vsIdx; if (ast.values[0].pin.selectors.length>0) { const vsOffset = genGetSigalOffset(ctx, vcIdx, ast.values[0].pin.name); const vsSizes = genGetSignalSizes(ctx, vcIdx, ast.values[0].pin.name); vsIdx = genGetOffset(ctx, vsOffset, vsSizes, ast.values[0].pin.selectors ); } else { vsIdx = genGetSigalOffset(ctx, vcIdx, ast.values[0].pin.name); } const vVal = gen(ctx, ast.values[1]); genSetSignal(ctx, vcIdx, vsIdx, vVal); return vVal; } if (ast.values[0].type == "DECLARE") { lName = gen(ctx, ast.values[0]); if (ctx.error) return; sels = []; } else { lName = ast.values[0].name; sels = ast.values[0].selectors; } const left = getScope(ctx, lName); if (!left) return error(ctx, ast, "Variable does not exists: "+ast.values[0].name); if (ctx.conditionalCode && !left.used) { instantiateRef(ctx, lName, left.value); } // Component instantiation is already done. if (left.type == "COMPONENT") { ctx.last = lName; return; } const rName = gen(ctx, ast.values[1]); const right = getScope(ctx, rName); if (left.type == "SIGNAL") { let vsIdx; if (sels.length>0) { const vsOffset = genGetSigalOffset(ctx, "ctx->cIdx", lName); const vsSizes = genGetSignalSizes(ctx, "ctx->cIdx", lName); vsIdx = genGetOffset(ctx, vsOffset, vsSizes, sels ); } else { vsIdx = genGetSigalOffset(ctx, "ctx->cIdx", lName); } return genSetSignal(ctx, "ctx->cIdx", vsIdx, rName); } else if (left.type == "BIGINT") { if (left.used) { if (!right.used) { instantiateRef(ctx, rName); } ctx.code += `ctx->field->copy(${left.label}, ${right.label});\n`; } else { if (right.used) { instantiateRef(ctx, lName); ctx.code += `ctx->field->copy(${left.label}, ${right.label});\n`; } else { if (ctx.conditionalCode) { instantiateRef(ctx, lName); ctx.code += `ctx->field->copy(${left.label}, ${right.label});\n`; } else { left.value = right.value; } } } } else { return error(ctx, ast, "Assigning to invalid"); } return lName; } function genConstraint(ctx, ast) { const a = gen(ctx, ast.values[0]); if (ctx.error) return; const b = gen(ctx, ast.values[1]); if (ctx.error) return; const strErr = ast.fileName + ":" + ast.first_line + ":" + ast.first_column; ctx.code += `ctx->checkConstraint(${a}, ${b}, "${strErr}");`; } function genArray(ctx, ast) { let S = "["; for (let i=0; i0) S += ","; S += gen(ctx, ast.values[i]); } S+="]"; return S; } function genFunctionCall(ctx, ast) { let S = "["; for (let i=0; i0) S += ","; S += gen(ctx, ast.params[i]); } S+="]"; return `ctx.callFunction("${ast.name}", ${S})`; } function enterConditionalCode(ctx) { if (!ctx.conditionalCode) { ctx.conditionalCode = 1; ctx.conditionalCodeHeader = ""; ctx.code += "__CONDITIONAL_CODE_HEADER__"; } else { ctx.conditionalCode ++; } } function leaveConditionalCode(ctx) { assert(ctx.conditionalCode, "Leaving conditional code too many times"); ctx.conditionalCode --; if (!ctx.conditionalCode) { ctx.code = ctx.code.replace("__CONDITIONAL_CODE_HEADER__", "// Conditional Code Header\n" + ctx.conditionalCodeHeader+ "\n"); delete ctx.conditionalCodeHeader; delete ctx.conditionalCode; } } function genFor(ctx, ast) { genForSrcComment(ctx, ast); let inLoop = false; ctx.scopes.push({_prefix : ""}); gen(ctx, ast.init); if (ctx.error) return; let end=false; let condVar; const condName = gen(ctx, ast.condition); const cond = getScope(ctx, condName); if (cond.used) { inLoop = true; enterConditionalCode(ctx); condVar = newRef(ctx, "INT", "_cond"); instantiateRef(ctx, condVar); ctx.code += `${condVar} = ctx->field->isTrue(${condName});\n` + `while (${condVar}) {\n`; } else { if ((typeof cond.value == "undefined")||(cond.value == null)) return error(ctx, ast, "condition value not assigned"); if (cond.value.isZero()) end=true; } while (!end) { const oldCode = ctx.code; ctx.code = ""; if (ast.body.type != "BLOCK") genSrcComment(ctx, ast.body); gen(ctx, ast.body); if (ctx.error) return; gen(ctx, ast.step); if (ctx.error) return; const condName2 = gen(ctx, ast.condition); const cond2 = getScope(ctx, condName2); if (!inLoop) { if (cond2.used) { ctx.code = oldCode + ctx.code; inLoop = true; enterConditionalCode(ctx); condVar = newRef(ctx, "INT", "_cond"); instantiateRef(ctx, condVar); ctx.code = oldCode + ctx.code + `${condVar} = ctx->field->isTrue(${condName2});\n` + `while (${condVar}) {\n`; } else { ctx.code = oldCode + ctx.code; if (cond2.value.isZero()) end=true; } } else { ctx.code = oldCode + utils.ident( ctx.code + `${condVar} = ctx->field->isTrue(${condName2});\n`); end=true; } } if (inLoop) { ctx.code += "}\n"; leaveConditionalCode(ctx); } ctx.scopes.pop(); } function genWhile(ctx, ast) { const condition = gen(ctx, ast.condition); if (ctx.error) return; const body = gen(ctx, ast.body); if (ctx.error) return; return `while (bigInt(${condition}).neq(bigInt(0))) {\n${body}\n}\n`; } function genCompute(ctx, ast) { const body = gen(ctx, ast.body); if (ctx.error) return; return `{\n${body}\n}\n`; } function genIf(ctx, ast) { const condition = gen(ctx, ast.condition); if (ctx.error) return; const thenBody = gen(ctx, ast.then); if (ctx.error) return; if (ast.else) { const elseBody = gen(ctx, ast.else); if (ctx.error) return; return `if (bigInt(${condition}).neq(bigInt(0))) {\n${thenBody}\n} else {\n${elseBody}\n}\n`; } else { return `if (bigInt(${condition}).neq(bigInt(0))) {\n${thenBody}\n}\n`; } } function genReturn(ctx, ast) { const value = gen(ctx, ast.value); if (ctx.error) return; return `return ${value};`; } function genSignalAssignConstrain(ctx, ast) { const res = genVarAssignement(ctx, ast); // genConstraint(ctx, ast); return res; // return genVarAssignement(ctx, ast); } function genVarAddAssignement(ctx, ast) { return genVarAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "+", values: ast.values}]}); } function genVarMulAssignement(ctx, ast) { return genVarAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "*", values: ast.values}]}); } function genPlusPlusRight(ctx, ast) { 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__)`; } function genPlusPlusLeft(ctx, ast) { return genVarAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "+", values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}]}]}); } function genMinusMinusRight(ctx, ast) { 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__)`; } function genMinusMinusLeft(ctx, ast) { return genVarAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "-", values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}]}]}); } function genBinaryOp(ctx, ast, op) { let aName = gen(ctx, ast.values[0]); if (ctx.error) return; const a = getScope(ctx, aName); let bName = gen(ctx, ast.values[1]); if (ctx.error) return; const b = getScope(ctx, bName); if ((!a.used)&&(typeof a.value == "undefined")) return error(ctx, ast, "Using a not assigned varialble: "+aName); if ((!b.used)&&(typeof b.value == "undefined")) return error(ctx, ast, "Using a not assigned varialble: "+bName); let rName; if (a.used || b.used) { if (!a.used) { aName = instantiateConstant(ctx, a.value); } if (!b.used) { bName = instantiateConstant(ctx, b.value); } rName = newRef(ctx, "BIGINT", "_tmp"); instantiateRef(ctx, rName); ctx.code += `ctx->field->${op}(${rName},${aName}, ${bName});\n`; } else { rName = newRef(ctx, "BIGINT", "_tmp", ctx.field[op](a.value, b.value)); } return rName; } function genMul(ctx, ast) { const a = gen(ctx, ast.values[0]); if (ctx.error) return; const b = gen(ctx, ast.values[1]); if (ctx.error) return; return `bigInt(${a}).mul(bigInt(${b})).mod(__P__)`; } function genSub(ctx, ast) { const a = gen(ctx, ast.values[0]); if (ctx.error) return; const b = gen(ctx, ast.values[1]); if (ctx.error) return; return `bigInt(${a}).add(__P__).sub(bigInt(${b})).mod(__P__)`; } function genDiv(ctx, ast) { const a = gen(ctx, ast.values[0]); if (ctx.error) return; const b = gen(ctx, ast.values[1]); if (ctx.error) return; return `bigInt(${a}).mul( bigInt(${b}).inverse(__P__) ).mod(__P__)`; } function genIDiv(ctx, ast) { const a = gen(ctx, ast.values[0]); if (ctx.error) return; const b = gen(ctx, ast.values[1]); if (ctx.error) return; return `bigInt(${a}).div( bigInt(${b}))`; } function genExp(ctx, ast) { const a = gen(ctx, ast.values[0]); if (ctx.error) return; const b = gen(ctx, ast.values[1]); if (ctx.error) return; return `bigInt(${a}).modPow(bigInt(${b}), __P__)`; } function genBAnd(ctx, ast) { const a = gen(ctx, ast.values[0]); if (ctx.error) return; const b = gen(ctx, ast.values[1]); if (ctx.error) return; return `bigInt(${a}).and(bigInt(${b})).and(__MASK__)`; } function genAnd(ctx, ast) { const a = gen(ctx, ast.values[0]); if (ctx.error) return; const b = gen(ctx, ast.values[1]); if (ctx.error) return; return `((bigInt(${a}).neq(bigInt(0)) && bigInt(${b}).neq(bigInt(0))) ? bigInt(1) : bigInt(0))`; } function genOr(ctx, ast) { const a = gen(ctx, ast.values[0]); if (ctx.error) return; const b = gen(ctx, ast.values[1]); if (ctx.error) return; return `((bigInt(${a}).neq(bigInt(0)) || bigInt(${b}).neq(bigInt(0))) ? bigInt(1) : bigInt(0))`; } function genShl(ctx, ast) { const a = gen(ctx, ast.values[0]); if (ctx.error) return; const b = gen(ctx, ast.values[1]); if (ctx.error) return; return `bigInt(${b}).greater(bigInt(256)) ? 0 : bigInt(${a}).shl(bigInt(${b})).and(__MASK__)`; } function genShr(ctx, ast) { const a = gen(ctx, ast.values[0]); if (ctx.error) return; const b = gen(ctx, ast.values[1]); if (ctx.error) return; return `bigInt(${b}).greater(bigInt(256)) ? 0 : bigInt(${a}).shr(bigInt(${b})).and(__MASK__)`; } function genMod(ctx, ast) { const a = gen(ctx, ast.values[0]); if (ctx.error) return; const b = gen(ctx, ast.values[1]); if (ctx.error) return; return `bigInt(${a}).mod(bigInt(${b}))`; } function genLt(ctx, ast) { const a = gen(ctx, ast.values[0]); if (ctx.error) return; const b = gen(ctx, ast.values[1]); if (ctx.error) return; return `bigInt(${a}).lt(bigInt(${b})) ? 1 : 0`; } function genGt(ctx, ast) { const a = gen(ctx, ast.values[0]); if (ctx.error) return; const b = gen(ctx, ast.values[1]); if (ctx.error) return; return `bigInt(${a}).gt(bigInt(${b})) ? 1 : 0`; } function genLte(ctx, ast) { const a = gen(ctx, ast.values[0]); if (ctx.error) return; const b = gen(ctx, ast.values[1]); if (ctx.error) return; return `bigInt(${a}).lesserOrEquals(bigInt(${b})) ? 1 : 0`; } function genGte(ctx, ast) { const a = gen(ctx, ast.values[0]); if (ctx.error) return; const b = gen(ctx, ast.values[1]); if (ctx.error) return; return `bigInt(${a}).greaterOrEquals(bigInt(${b})) ? 1 : 0`; } function genEq(ctx, ast) { const a = gen(ctx, ast.values[0]); if (ctx.error) return; const b = gen(ctx, ast.values[1]); if (ctx.error) return; return `(bigInt(${a}).eq(bigInt(${b})) ? 1 : 0)`; } function genNeq(ctx, ast) { const a = gen(ctx, ast.values[0]); if (ctx.error) return; const b = gen(ctx, ast.values[1]); if (ctx.error) return; return `(bigInt(${a}).eq(bigInt(${b})) ? 0 : 1)`; } function genUMinus(ctx, ast) { const a = gen(ctx, ast.values[0]); if (ctx.error) return; return `__P__.sub(bigInt(${a})).mod(__P__)`; } function genTerCon(ctx, ast) { const a = gen(ctx, ast.values[0]); if (ctx.error) return; const b = gen(ctx, ast.values[1]); if (ctx.error) return; const c = gen(ctx, ast.values[2]); if (ctx.error) return; return `bigInt(${a}).neq(bigInt(0)) ? (${b}) : (${c})`; } function genInclude(ctx, ast) { return ast.block ? gen(ctx, ast.block) : ""; }