mirror of
https://github.com/arnaucube/circom.git
synced 2026-02-07 03:06:42 +01:00
C generation
This commit is contained in:
52
src/buildwasm.js
Normal file
52
src/buildwasm.js
Normal file
@@ -0,0 +1,52 @@
|
||||
|
||||
const ModuleBuilder = require("wasmbuilder").ModuleBuilder;
|
||||
const gen = require("./gencode");
|
||||
|
||||
|
||||
module.exports = function buildWasm(ctx) {
|
||||
|
||||
const fDefined = {};
|
||||
|
||||
ctx.module = new ModuleBuilder();
|
||||
for (let f in ctx.functions) {
|
||||
ctx.f = ctx.module.addFunction(f);
|
||||
ctx.c = ctx.f.getCodeBuilder();
|
||||
|
||||
ctx.scope = {};
|
||||
for (let p in ctx.functions[f].params) {
|
||||
const param = ctx.functions[f].params[p];
|
||||
ctx.f.addParam(param.name, "i32");
|
||||
ctx.scope[param.name] = {
|
||||
type: "PARAM",
|
||||
sels: param.sels,
|
||||
getter: () => { return ctx.c.getLocal(param.name); },
|
||||
setter: (v) => { return ctx.c.setLocal(param.name, v); }
|
||||
};
|
||||
}
|
||||
|
||||
gen(ctx, ctx.functions[f].block);
|
||||
}
|
||||
|
||||
for (let i=0; i<ctx.components.length; i++) {
|
||||
const h = hashComponentCall(ctx, i);
|
||||
const fName = ctx.components[i].temlate+"_"+h;
|
||||
if (!fDefined[fName]) {
|
||||
|
||||
ctx.f = ctx.module.addFunction(fName);
|
||||
ctx.c = ctx.f.getCodeBuilder();
|
||||
|
||||
ctx.scope = {};
|
||||
for (let p in ctx.components[i].params) {
|
||||
ctx.scope[p] = createConstant(ctx, ctx.components[i].params[p]);
|
||||
}
|
||||
|
||||
gen(ctx, ctx.templates[ctx.components[i].temlate].block);
|
||||
}
|
||||
ctx.components[i].f = fName;
|
||||
}
|
||||
};
|
||||
|
||||
function buildSetSignal(ctx) {
|
||||
|
||||
|
||||
}
|
||||
377
src/c_build.js
Normal file
377
src/c_build.js
Normal file
@@ -0,0 +1,377 @@
|
||||
/*
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const utils = require("./utils");
|
||||
const assert = require("assert");
|
||||
const gen = require("./c_gen");
|
||||
|
||||
module.exports = buildC;
|
||||
|
||||
|
||||
function buildC(ctx) {
|
||||
ctx.code = "";
|
||||
ctx.tmpNames = {};
|
||||
ctx.getTmpName = getTmpName;
|
||||
ctx.codes_sizes = [];
|
||||
ctx.definedSizes = {};
|
||||
ctx.addSizes = addSizes;
|
||||
|
||||
const entryTables = buildEntryTables(ctx);
|
||||
const code = buildCode(ctx);
|
||||
const compnentsArray = buildComponentsArray(ctx);
|
||||
|
||||
const headder = buildHeader(ctx);
|
||||
const sizes = buildSizes(ctx);
|
||||
const mapIsInput = buildMapIsInput(ctx);
|
||||
const wit2Sig = buildWit2Sig(ctx);
|
||||
const circuitVar = buildCircuitVar(ctx);
|
||||
|
||||
return "" +
|
||||
headder + "\n" +
|
||||
sizes + "\n" +
|
||||
entryTables + "\n" +
|
||||
code + "\n" +
|
||||
compnentsArray + "\n" +
|
||||
mapIsInput + "\n" +
|
||||
wit2Sig +"\n" +
|
||||
circuitVar;
|
||||
}
|
||||
|
||||
function buildEntryTables(ctx) {
|
||||
|
||||
const codes_hashMaps = [];
|
||||
const codes_componentEntries = [];
|
||||
const definedHashTables = {};
|
||||
for (let i=0; i<ctx.components.length; i++) {
|
||||
const {htName, htMap} = addHashTable(i);
|
||||
|
||||
let code = "";
|
||||
const componentEntriesTableName = ctx.getTmpName("entryTable_" + ctx.components[i].template);
|
||||
|
||||
code += `Circom_ComponentEntry ${componentEntriesTableName}[${htMap.length}] = {\n`;
|
||||
for (let j=0; j<htMap.length; j++) {
|
||||
const entry = ctx.components[i].names.o[htMap[j]];
|
||||
code += j>0 ? " ," : " ";
|
||||
const sizeName = ctx.addSizes(entry.sizes);
|
||||
|
||||
const ty = entry.type == "S" ? "_typeSignal" : "_typeComponent";
|
||||
code += `{${entry.offset},${sizeName}, ${ty}}\n`;
|
||||
}
|
||||
code += "};\n";
|
||||
codes_componentEntries.push(code);
|
||||
|
||||
ctx.components[i].htName = htName;
|
||||
ctx.components[i].etName = componentEntriesTableName;
|
||||
}
|
||||
|
||||
|
||||
return "" +
|
||||
"// HashMaps\n" +
|
||||
codes_hashMaps.join("\n") + "\n" +
|
||||
"\n" +
|
||||
"// Component Entries\n" +
|
||||
codes_componentEntries.join("\n") + "\n" +
|
||||
"\n";
|
||||
|
||||
function addHashTable(cIdx) {
|
||||
const keys = Object.keys(ctx.components[cIdx].names.o);
|
||||
assert(keys.length<128);
|
||||
keys.sort((a,b) => ((a>b) ? 1 : -1));
|
||||
const h = utils.fnvHash(keys.join(","));
|
||||
if (definedHashTables[h]) return definedHashTables[h];
|
||||
definedHashTables[h] = {};
|
||||
definedHashTables[h].htName = ctx.getTmpName("ht_"+ctx.components[cIdx].template);
|
||||
definedHashTables[h].htMap = [];
|
||||
const t = [];
|
||||
for (let i=0; i<keys.length; i++) {
|
||||
definedHashTables[h].htMap[i] = keys[i];
|
||||
|
||||
const h2 = utils.fnvHash(keys[i]);
|
||||
let pos = parseInt(h2.slice(-2), 16);
|
||||
while (t[pos]) pos = (pos + 1) % 256;
|
||||
t[pos] = [h2, i];
|
||||
}
|
||||
let code = `Circom_HashEntry ${definedHashTables[h].htName}[256] = {`;
|
||||
for (let i=0; i<256; i++) {
|
||||
code += i>0 ? "," : "";
|
||||
if (t[i]) {
|
||||
code += `{0x${t[i][0]}LL, ${t[i][1]}} /* ${keys[t[i][1]]} */`;
|
||||
} else {
|
||||
code += "{0,0}";
|
||||
}
|
||||
}
|
||||
code += "};\n";
|
||||
|
||||
codes_hashMaps.push(code);
|
||||
|
||||
return definedHashTables[h];
|
||||
}
|
||||
}
|
||||
|
||||
function buildCode(ctx) {
|
||||
const globalNames = ctx.tmpNames;
|
||||
|
||||
const fDefined = {};
|
||||
|
||||
const functions = [];
|
||||
for (let f in ctx.functions) {
|
||||
ctx.scope = {};
|
||||
const paramsList = [];
|
||||
for (let p in ctx.functions[f].params) {
|
||||
const param = ctx.functions[f].params[p];
|
||||
paramsList.push("POINTER "+param.name);
|
||||
|
||||
ctx.scope[param.name] = {
|
||||
type: "LOCAL",
|
||||
sels: param.sels,
|
||||
getter: () => { return param.name; },
|
||||
};
|
||||
}
|
||||
|
||||
ctx.code += "void "+f+"(POINTER _ret, "+paramsList.join(",")+") {\n";
|
||||
|
||||
ctx.code += gen(ctx, ctx.functions[f].block);
|
||||
ctx.code += "}";
|
||||
}
|
||||
|
||||
for (let i=0; i<ctx.components.length; i++) {
|
||||
const h = hashComponentCall(ctx, i);
|
||||
const fName = ctx.components[i].template+"_"+h;
|
||||
if (!fDefined[fName]) {
|
||||
|
||||
|
||||
const scope = {_prefix : ""};
|
||||
ctx.scopes = [scope];
|
||||
ctx.nScopes = 0;
|
||||
ctx.code = "";
|
||||
ctx.codeHeader = "// Header\n";
|
||||
ctx.codeFooter = "// Footer\n";
|
||||
ctx.tmpNames = Object.assign({},globalNames);
|
||||
|
||||
for (let p in ctx.components[i].params) {
|
||||
newRef(ctx, "BIGINT", p, ctx.components[i].params[p]);
|
||||
}
|
||||
|
||||
gen(ctx, ctx.templates[ctx.components[i].template].block);
|
||||
|
||||
const S = `void ${fName}(Circom_CalcWit *ctx) {\n` +
|
||||
utils.ident(
|
||||
ctx.codeHeader + "\n" +
|
||||
ctx.code + "\n" +
|
||||
ctx.codeFooter
|
||||
) +
|
||||
"}\n";
|
||||
|
||||
functions.push(S);
|
||||
}
|
||||
ctx.components[i].fnName = fName;
|
||||
}
|
||||
|
||||
return functions.join("\n");
|
||||
}
|
||||
|
||||
function buildComponentsArray(ctx) {
|
||||
|
||||
const ccodes = [];
|
||||
ccodes.push(`Circom_Component _components[${ctx.components.length}] = {\n`);
|
||||
for (let i=0; i< ctx.components.length; i++) {
|
||||
ccodes.push(i>0 ? " ," : " ");
|
||||
ccodes.push(`{${ctx.components[i].htName},${ctx.components[i].etName},${ctx.components[i].fnName}, ${ctx.components[i].nInSignals}}\n`);
|
||||
}
|
||||
ccodes.push("};\n");
|
||||
const codeComponents = ccodes.join("");
|
||||
|
||||
return "" +
|
||||
"// Components\n" +
|
||||
codeComponents +
|
||||
"\n";
|
||||
}
|
||||
|
||||
|
||||
function buildHeader(ctx) {
|
||||
return "#include \"circom.h\"\n" +
|
||||
"#include \"calcwit.h\"\n" +
|
||||
`#define NSignals ${ctx.signals.length}\n` +
|
||||
`#define NComponents ${ctx.components.length}\n` +
|
||||
`#define NInputs ${ctx.components[ ctx.getComponentIdx("main") ].nInSignals}\n`+
|
||||
`#define NOutputs ${ctx.totals[ ctx.stOUTPUT ]}\n`+
|
||||
`#define NVars ${ctx.totals[ctx.stONE] + ctx.totals[ctx.stOUTPUT] + ctx.totals[ctx.stPUBINPUT] + ctx.totals[ctx.stPRVINPUT] + ctx.totals[ctx.stINTERNAL]}\n` +
|
||||
"\n";
|
||||
}
|
||||
|
||||
function buildSizes(ctx) {
|
||||
return "// Sizes\n" +
|
||||
ctx.codes_sizes.join("\n");
|
||||
}
|
||||
|
||||
|
||||
function buildMapIsInput(ctx) {
|
||||
const arr = [];
|
||||
let line = "";
|
||||
let acc = 0;
|
||||
let i;
|
||||
for (i=0; i<ctx.signals.length; i++) {
|
||||
if (ctx.signals[i].o & ctx.IN) {
|
||||
acc = acc | (1 << (i%32) );
|
||||
}
|
||||
if ((i+1)%32==0) {
|
||||
line += (i>31) ? "," : " ";
|
||||
line += toHex(acc);
|
||||
acc = 0;
|
||||
if ( i % (32*64) == 0) {
|
||||
arr.push(line);
|
||||
line = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((i%32) != 0) {
|
||||
line += (i>31) ? "," : " ";
|
||||
line += toHex(acc);
|
||||
}
|
||||
if (line != "") {
|
||||
arr.push(line);
|
||||
}
|
||||
|
||||
return "// mapIsArray\n" +
|
||||
`u32 _mapIsInput[${Math.floor((ctx.signals.length-1) / 32)+1}] = {\n`+
|
||||
arr.join("\n") + "\n" +
|
||||
"};\n";
|
||||
|
||||
function toHex(number) {
|
||||
if (number < 0) number = 0xFFFFFFFF + number + 1;
|
||||
let S=number.toString(16).toUpperCase();
|
||||
while (S.length<8) S = "0" + S;
|
||||
return "0x"+S;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function buildWit2Sig(ctx) {
|
||||
const codes = [];
|
||||
const NVars =
|
||||
ctx.totals[ctx.stONE] +
|
||||
ctx.totals[ctx.stOUTPUT] +
|
||||
ctx.totals[ctx.stPUBINPUT] +
|
||||
ctx.totals[ctx.stPRVINPUT] +
|
||||
ctx.totals[ctx.stINTERNAL];
|
||||
const arr = Array(NVars);
|
||||
for (let i=0; i<ctx.signals.length; i++) {
|
||||
const outIdx = ctx.signals[i].id;
|
||||
if (typeof outIdx == "undefined") continue;
|
||||
if (ctx.signals[i].e>=0) continue; // If has an alias, continue..
|
||||
assert(outIdx<NVars);
|
||||
if (typeof arr[ctx.signals[i].id] == "undefined") {
|
||||
arr[outIdx] = i;
|
||||
}
|
||||
}
|
||||
codes.push("// Signal Table\n");
|
||||
codes.push(`int _wit2sig[${NVars}] = {\n`);
|
||||
let code = "";
|
||||
for (let i=0; i<NVars; i++) {
|
||||
code += (i>0) ? ",": " ";
|
||||
code += arr[i];
|
||||
if ((i>0)&&(i%64 == 0)) {
|
||||
if (code != "") codes.push(code + "\n");
|
||||
codes.push(code);
|
||||
code =0;
|
||||
}
|
||||
}
|
||||
if (code != "") codes.push(code + "\n");
|
||||
codes.push("};\n");
|
||||
|
||||
return codes.join("");
|
||||
|
||||
}
|
||||
|
||||
|
||||
function buildCircuitVar() {
|
||||
return "Circom_Circuit _circuit = {\n" +
|
||||
" NSignals,\n"+
|
||||
" NComponents,\n"+
|
||||
" NInputs,\n"+
|
||||
" NOutputs,\n"+
|
||||
" NVars,\n"+
|
||||
" _wit2sig,\n"+
|
||||
" _components,\n"+
|
||||
" _mapIsInput\n"+
|
||||
"};\n";
|
||||
}
|
||||
|
||||
|
||||
|
||||
function hashComponentCall(ctx, cIdx) {
|
||||
// TODO: At the moment generate a diferent function for each instance of the component
|
||||
return cIdx;
|
||||
}
|
||||
|
||||
function getTmpName(_suggestedName) {
|
||||
let suggestedName;
|
||||
if (_suggestedName) {
|
||||
suggestedName = trimUnderscore(_suggestedName);
|
||||
} else {
|
||||
suggestedName = "tmp";
|
||||
}
|
||||
|
||||
if (typeof(this.tmpNames[suggestedName]) == "undefined") {
|
||||
this.tmpNames[suggestedName] = 1;
|
||||
return "_"+suggestedName;
|
||||
} else {
|
||||
const name = "_" + suggestedName + "_" + this.tmpNames[suggestedName];
|
||||
this.tmpNames[suggestedName]++;
|
||||
return name;
|
||||
}
|
||||
|
||||
function trimUnderscore(str) {
|
||||
let p1=0;
|
||||
while ((p1 < str.length)&&(str[p1] == "_")) p1++;
|
||||
let p2=str.length;
|
||||
while ((p2 > 0)&&(str[p2-1] == "_")) p2--;
|
||||
|
||||
return str.slice(p1,p2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function addSizes(_sizes) {
|
||||
const sizes = _sizes || [];
|
||||
let name = "sizes";
|
||||
for (let i=0; i<sizes.length;i++) {
|
||||
name+="_"+sizes[i];
|
||||
}
|
||||
if (name=="sizes") name="sizes_0";
|
||||
|
||||
if (this.definedSizes[name]) return this.definedSizes[name];
|
||||
|
||||
const labelName = this.getTmpName(name);
|
||||
this.definedSizes[name] = labelName;
|
||||
|
||||
const accSizes = utils.accSizes(sizes);
|
||||
|
||||
let code = `Circom_Size ${labelName}[${accSizes.length}] = {`;
|
||||
for (let i=0; i<accSizes.length; i++) {
|
||||
if (i>0) code += ",";
|
||||
code += accSizes[i];
|
||||
}
|
||||
code += "};\n";
|
||||
this.codes_sizes.push(code);
|
||||
|
||||
return labelName;
|
||||
}
|
||||
|
||||
941
src/c_gen.js
Normal file
941
src/c_gen.js
Normal file
@@ -0,0 +1,941 @@
|
||||
const bigInt = require("big-integer");
|
||||
const utils = require("./utils");
|
||||
|
||||
module.exports = gen;
|
||||
|
||||
|
||||
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("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) {
|
||||
const v = getScope(ctx, name);
|
||||
if (!v.stack) return error("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`;
|
||||
ctx.code += `${v.label} = ctx->allocBigInts(${labelSize});\n`;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
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 genAdd(ctx, ast);
|
||||
} 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 genLt(ctx, ast);
|
||||
} 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) {
|
||||
const oldCode = ctx.code;
|
||||
let res = null;
|
||||
ctx.code = "";
|
||||
for (let i=0; i<ast.statements.length; i++) {
|
||||
if (["BLOCK", "COMPUTE", "FOR", "WHILE", "IF"].indexOf(ast.statements[i].type)<0) {
|
||||
genSrcComment(ctx, ast.statements[i]);
|
||||
}
|
||||
res = gen(ctx, ast.statements[i]);
|
||||
if (ctx.error) return;
|
||||
}
|
||||
if (ctx.scopes.length>1) {
|
||||
ctx.code = oldCode + `{\n${utils.ident(ctx.code)}}\n`;
|
||||
} else {
|
||||
ctx.code = oldCode + ctx.code;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function genSrcComment(ctx, ast) {
|
||||
let codes = [];
|
||||
const fl= ast.first_line-1;
|
||||
const ll = ast.last_line-1;
|
||||
const fc = ast.first_column-1;
|
||||
const lc = ast.last_column;
|
||||
for (let i=fl; i<=ll; i++) {
|
||||
const p1 = i==fl ? fc : 0;
|
||||
const p2 = i==ll ? lc : -1;
|
||||
codes.push(ctx.includedFiles[ctx.fileName][i].slice(p1, p2>=0 ? p2 : undefined));
|
||||
}
|
||||
ctx.code += "\n/* "+codes.join("\n")+"*/\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 (ctx.scope[varName]) 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 = ctx.scope[varName] = {
|
||||
stack: true,
|
||||
type: "BIGINT",
|
||||
sizes: vSizes,
|
||||
label: labelName
|
||||
};
|
||||
|
||||
scope[varName] = res;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
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; i<sels.length; i++) {
|
||||
const vIdx = gen(ctx, sels[i]);
|
||||
const iIdx = getScope(ctx, vIdx);
|
||||
|
||||
if ((!iIdx.used) && (!iSizes.used)) {
|
||||
rN = rN + iIdx.value * iSizes.sizes[i+1];
|
||||
} else {
|
||||
if (rN>0) {
|
||||
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 );
|
||||
if (v.used) {
|
||||
if (vOffset == 0) {
|
||||
return ast.name;
|
||||
} else {
|
||||
const res = newRef(ctx, "BIGINT", "_v");
|
||||
instantiateRef(ctx, res);
|
||||
ctx.code += `${res} = ${ast.name} + ${vOffset};\n`;
|
||||
return res;
|
||||
}
|
||||
} else {
|
||||
if (typeof(vOffset) == "string") {
|
||||
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; i<ast.selectors.length; i++) {
|
||||
sels.push(gen(ctx, ast.selectors[i]));
|
||||
if (ctx.error) return;
|
||||
}
|
||||
|
||||
if (!v) {
|
||||
return error(ctx, ast, "Invalid left operand");
|
||||
}
|
||||
if (v.type == "VARIABLE") {
|
||||
return `ctx.getVar("${ast.name}",[${sels.join(",")}])`;
|
||||
} else if (v.type == "SIGNAL") {
|
||||
return `ctx.getSignal("${ast.name}", [${sels.join(",")}])`;
|
||||
} else {
|
||||
error(ctx, ast, "Invalid Variable type");
|
||||
}
|
||||
}
|
||||
|
||||
function instantiateConstant(ctx, value) {
|
||||
const flatedValue = utils.flatArray(value);
|
||||
const res = ctx.getTmpName("_const");
|
||||
ctx.codeHeader += `PBigInt ${res};\n`;
|
||||
ctx.code += `${res.label} = ctx->allocBigInts(${flatedValue.length});\n`;
|
||||
for (let i=0; i<flatedValue.length; i++) {
|
||||
ctx.code += `mpz_init_set_str(${res.label}[${i}], ${flatedValue[i].toString(16)}, 16);\n`;
|
||||
}
|
||||
ctx.codeFooter += `ctx->freeBigInts(${res.label});\n`;
|
||||
return res;
|
||||
}
|
||||
|
||||
function genPin(ctx, ast) {
|
||||
let vcIdx;
|
||||
if (ast.component.selectors.length>0) {
|
||||
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) {
|
||||
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);
|
||||
|
||||
// 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 == "VAR") {
|
||||
if (left.used) {
|
||||
if (!right.used) {
|
||||
instantiateRef(ctx, rName);
|
||||
}
|
||||
ctx.code += `BigInt_copy(${left.label}, ${right.label});\n`;
|
||||
return lName;
|
||||
} else {
|
||||
if (right.used) {
|
||||
instantiateRef(ctx, lName);
|
||||
ctx.code += `BigInt_copy(${left.label}, ${right.label});\n`;
|
||||
return lName;
|
||||
} else {
|
||||
if (!left.value) {
|
||||
left.value = right.value;
|
||||
} else {
|
||||
if (!left.value.equals(right.value)) {
|
||||
if (ctx.scopes.length > 1) {
|
||||
instantiateRef(ctx, lName);
|
||||
ctx.code += `BigInt_copy(${left.label}, ${right.label});\n`;
|
||||
} else {
|
||||
left.value = right.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return error(ctx, ast, "Assigning to invalid");
|
||||
}
|
||||
}
|
||||
|
||||
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; i<ast.values.length; i++) {
|
||||
if (i>0) S += ",";
|
||||
S += gen(ctx, ast.values[i]);
|
||||
}
|
||||
S+="]";
|
||||
return S;
|
||||
}
|
||||
|
||||
|
||||
function genFunctionCall(ctx, ast) {
|
||||
let S = "[";
|
||||
for (let i=0; i<ast.params.length; i++) {
|
||||
if (i>0) S += ",";
|
||||
S += gen(ctx, ast.params[i]);
|
||||
}
|
||||
S+="]";
|
||||
|
||||
return `ctx.callFunction("${ast.name}", ${S})`;
|
||||
}
|
||||
|
||||
function genFor(ctx, ast) {
|
||||
ctx.scopes.push({});
|
||||
const init = gen(ctx, ast.init);
|
||||
if (ctx.error) return;
|
||||
const condition = gen(ctx, ast.condition);
|
||||
if (ctx.error) return;
|
||||
const step = gen(ctx, ast.step);
|
||||
if (ctx.error) return;
|
||||
const body = gen(ctx, ast.body);
|
||||
if (ctx.error) return;
|
||||
ctx.scopes.pop();
|
||||
return `for (${init};bigInt(${condition}).neq(bigInt(0));${step}) { \n${body}\n }\n`;
|
||||
}
|
||||
|
||||
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 genAdd(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(bigInt(${b})).mod(__P__)`;
|
||||
}
|
||||
|
||||
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) : "";
|
||||
}
|
||||
|
||||
|
||||
119
src/c_tester.js
Normal file
119
src/c_tester.js
Normal file
@@ -0,0 +1,119 @@
|
||||
const chai = require("chai");
|
||||
const assert = chai.assert;
|
||||
|
||||
const fs = require("fs");
|
||||
var tmp = require("tmp-promise");
|
||||
const path = require("path");
|
||||
const compiler = require("./compiler");
|
||||
const util = require("util");
|
||||
const exec = util.promisify(require("child_process").exec);
|
||||
|
||||
const stringifyBigInts = require("snarkjs").stringifyBigInts;
|
||||
const unstringifyBigInts = require("snarkjs").unstringifyBigInts;
|
||||
const bigInt = require("snarkjs").bigInt;
|
||||
|
||||
module.exports = c_tester;
|
||||
|
||||
|
||||
async function c_tester(circomFile, mainComponent, _options) {
|
||||
tmp.setGracefulCleanup();
|
||||
mainComponent = mainComponent || "main";
|
||||
|
||||
const dir = await tmp.dir({prefix: "circom_", unsafeCleanup: true });
|
||||
|
||||
const baseName = path.basename(circomFile, ".circom");
|
||||
const options = Object.assign({}, _options);
|
||||
|
||||
options.cSourceWriteStream = fs.createWriteStream(path.join(dir.path, baseName + ".cpp"));
|
||||
options.symWriteStream = fs.createWriteStream(path.join(dir.path, baseName + ".sym"));
|
||||
options.mainComponent = mainComponent;
|
||||
await compiler(circomFile, options);
|
||||
|
||||
const cdir = path.join(__dirname, "..", "c");
|
||||
await exec("g++" +
|
||||
` ${path.join(dir.path, baseName + ".cpp")} ` +
|
||||
` ${path.join(cdir, "main.cpp")}` +
|
||||
` ${path.join(cdir, "calcwit.cpp")}` +
|
||||
` ${path.join(cdir, "utils.cpp")}` +
|
||||
` -o ${path.join(dir.path, baseName)}` +
|
||||
` -I ${cdir}` +
|
||||
" -lgmp -std=c++11 -DSANITY_CHECK"
|
||||
);
|
||||
|
||||
console.log(dir.path);
|
||||
return new CTester(dir, baseName, mainComponent);
|
||||
}
|
||||
|
||||
class CTester {
|
||||
|
||||
constructor(dir, baseName, mainComponent) {
|
||||
this.dir=dir;
|
||||
this.baseName = baseName;
|
||||
this.mainComponent = mainComponent;
|
||||
}
|
||||
|
||||
async release() {
|
||||
await this.dir.cleanup();
|
||||
}
|
||||
|
||||
async calculateWitness(input) {
|
||||
await fs.promises.writeFile(
|
||||
path.join(this.dir.path, "in.json"),
|
||||
JSON.stringify(stringifyBigInts(input), null, 1)
|
||||
);
|
||||
await exec(`${path.join(this.dir.path, this.baseName)}` +
|
||||
` ${path.join(this.dir.path, "in.json")}` +
|
||||
` ${path.join(this.dir.path, "out.json")}`
|
||||
);
|
||||
const resStr = await fs.promises.readFile(
|
||||
path.join(this.dir.path, "out.json")
|
||||
);
|
||||
|
||||
const res = unstringifyBigInts(JSON.parse(resStr));
|
||||
return res;
|
||||
}
|
||||
|
||||
async _loadSymbols() {
|
||||
this.symbols = {};
|
||||
const symsStr = await fs.promises.readFile(
|
||||
path.join(this.dir.path, this.baseName + ".sym"),
|
||||
"utf8"
|
||||
);
|
||||
const lines = symsStr.split("\n");
|
||||
for (let i=0; i<lines.length; i++) {
|
||||
const arr = lines[i].split(",");
|
||||
if (arr.length!=3) continue;
|
||||
this.symbols[arr[2]] = {
|
||||
idx: Number(arr[0]),
|
||||
idxWit: Number(arr[1])
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async assertOut(actualOut, expectedOut) {
|
||||
const self = this;
|
||||
if (!self.symbols) await self._loadSymbols();
|
||||
|
||||
checkObject("main", expectedOut);
|
||||
|
||||
function checkObject(prefix, eOut) {
|
||||
|
||||
if (Array.isArray(eOut)) {
|
||||
for (let i=0; i<eOut.length; i++) {
|
||||
checkObject(prefix + "["+i+"]", eOut[i]);
|
||||
}
|
||||
} else if (typeof eOut == "object") {
|
||||
for (let k in eOut) {
|
||||
checkObject(prefix + "."+k, eOut[k]);
|
||||
}
|
||||
} else {
|
||||
const ba = bigInt(actualOut[self.symbols[prefix].idxWit]).toString();
|
||||
const be = bigInt(eOut).toString();
|
||||
assert.strictEqual(ba, be, prefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
300
src/compiler.js
300
src/compiler.js
@@ -22,10 +22,12 @@ const path = require("path");
|
||||
const bigInt = require("big-integer");
|
||||
const __P__ = new bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
||||
const __MASK__ = new bigInt(2).pow(253).minus(1);
|
||||
const sONE = 0;
|
||||
const assert = require("assert");
|
||||
const gen = require("./gencode");
|
||||
const buildC = require("./c_build");
|
||||
const exec = require("./exec");
|
||||
const lc = require("./lcalgebra");
|
||||
const Ctx = require("./ctx");
|
||||
|
||||
module.exports = compile;
|
||||
|
||||
@@ -48,41 +50,34 @@ async function compile(srcFile, options) {
|
||||
|
||||
assert(ast.type == "BLOCK");
|
||||
|
||||
const ctx = {
|
||||
scopes: [{}],
|
||||
signals: {
|
||||
one: {
|
||||
fullName: "one",
|
||||
value: bigInt(1),
|
||||
equivalence: "",
|
||||
direction: ""
|
||||
}
|
||||
},
|
||||
currentComponent: "",
|
||||
constraints: [],
|
||||
components: {},
|
||||
templates: {},
|
||||
functions: {},
|
||||
functionParams: {},
|
||||
filePath: fullFilePath,
|
||||
fileName: fullFileName,
|
||||
verbose: options.verbose || false
|
||||
};
|
||||
const ctx = new Ctx();
|
||||
ctx.mainComponent = options.mainComponent || "main";
|
||||
ctx.filePath= fullFilePath;
|
||||
ctx.fileName= fullFileName;
|
||||
ctx.includedFiles = {};
|
||||
ctx.includedFiles[fullFileName] = src.split("\n");
|
||||
|
||||
ctx.verbose= options.verbose || false;
|
||||
|
||||
|
||||
exec(ctx, ast);
|
||||
|
||||
if (!ctx.components["main"]) {
|
||||
if (ctx.error) {
|
||||
throw(ctx.error);
|
||||
}
|
||||
|
||||
if (ctx.getComponentIdx(ctx.mainComponent)<0) {
|
||||
throw new Error("A main component must be defined");
|
||||
}
|
||||
|
||||
if (ctx.verbose) console.log("Classify Signals");
|
||||
classifySignals(ctx);
|
||||
|
||||
if (ctx.verbose) console.log("Reduce Constraints");
|
||||
if (ctx.verbose) console.log("Reduce Constants");
|
||||
reduceConstants(ctx);
|
||||
if (options.reduceConstraints) {
|
||||
|
||||
if (ctx.verbose) console.log("Reduce Constraints");
|
||||
// Repeat while reductions are performed
|
||||
let oldNConstrains = -1;
|
||||
while (ctx.constraints.length != oldNConstrains) {
|
||||
@@ -97,121 +92,134 @@ async function compile(srcFile, options) {
|
||||
throw(ctx.error);
|
||||
}
|
||||
|
||||
ctx.scopes = [{}];
|
||||
|
||||
const mainCode = gen(ctx,ast);
|
||||
if (options.cSourceWriteStream) {
|
||||
const cSrc = buildC(ctx);
|
||||
options.cSourceWriteStream.write(cSrc);
|
||||
}
|
||||
|
||||
// const mainCode = gen(ctx,ast);
|
||||
if (ctx.error) throw(ctx.error);
|
||||
|
||||
const def = buildCircuitDef(ctx, mainCode);
|
||||
if (options.r1csWriteStream) {
|
||||
buildR1cs(ctx, options.r1csWriteStream);
|
||||
}
|
||||
|
||||
if (options.symWriteStream) {
|
||||
buildSyms(ctx, options.symWriteStream);
|
||||
}
|
||||
|
||||
// const def = buildCircuitDef(ctx, mainCode);
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function classifySignals(ctx) {
|
||||
|
||||
const ERROR = 0xFFFF;
|
||||
|
||||
function priorize(t1, t2) {
|
||||
if ((t1 == "error") || (t2=="error")) return "error";
|
||||
if (t1 == "internal") {
|
||||
if ((t1 == ERROR) || (t2==ERROR)) return ERROR;
|
||||
if (t1 == ctx.stINTERNAL) {
|
||||
return t2;
|
||||
} else if (t2=="internal") {
|
||||
} else if (t2==ctx.stINTERNAL) {
|
||||
return t1;
|
||||
}
|
||||
if ((t1 == "one") || (t2 == "one")) return "one";
|
||||
if ((t1 == "constant") || (t2 == "constant")) return "constant";
|
||||
if (t1!=t2) return "error";
|
||||
if ((t1 == ctx.stONE) || (t2 == ctx.stONE)) return ctx.stONE;
|
||||
if ((t1 == ctx.stCONSTANT) || (t2 == ctx.stCONSTANT)) return ctx.stCONSTANT;
|
||||
if ((t1 == ctx.stDISCARDED) || (t2 == ctx.stDISCARDED)) return ctx.stDISCARDED;
|
||||
if (t1!=t2) return ERROR;
|
||||
return t1;
|
||||
}
|
||||
|
||||
// First classify the signals
|
||||
for (let s in ctx.signals) {
|
||||
const signal = ctx.signals[s];
|
||||
let tAll = "internal";
|
||||
let tAll = ctx.stINTERNAL;
|
||||
let lSignal = signal;
|
||||
let end = false;
|
||||
while (!end) {
|
||||
let t = lSignal.category || "internal";
|
||||
if (s == "one") {
|
||||
t = "one";
|
||||
} else if (lSignal.value) {
|
||||
t = "constant";
|
||||
} else if (lSignal.component=="main") {
|
||||
if (lSignal.direction == "IN") {
|
||||
if (lSignal.private) {
|
||||
t = "prvInput";
|
||||
let t = lSignal.c || ctx.stINTERNAL;
|
||||
if (s == 0) {
|
||||
t = ctx.stONE;
|
||||
} else if (lSignal.v) {
|
||||
t = ctx.stCONSTANT;
|
||||
} else if (lSignal.o & ctx.MAIN) {
|
||||
if (lSignal.o & ctx.IN) {
|
||||
if (lSignal.o & ctx.PRV) {
|
||||
t = ctx.stPRVINPUT;
|
||||
} else {
|
||||
t = "pubInput";
|
||||
t = ctx.stPUBINPUT;
|
||||
}
|
||||
} else if (lSignal.direction == "OUT") {
|
||||
t = "output";
|
||||
} else if (lSignal.o & ctx.OUT) {
|
||||
t = ctx.stOUTPUT;
|
||||
}
|
||||
}
|
||||
tAll = priorize(t,tAll);
|
||||
if (lSignal.equivalence) {
|
||||
lSignal = ctx.signals[lSignal.equivalence];
|
||||
if (lSignal.e>=0) {
|
||||
lSignal = ctx.signals[lSignal.e];
|
||||
} else {
|
||||
end=true;
|
||||
}
|
||||
}
|
||||
if (tAll == "error") {
|
||||
if (tAll == ERROR) {
|
||||
throw new Error("Incompatible types in signal: " + s);
|
||||
}
|
||||
lSignal.category = tAll;
|
||||
lSignal.c = tAll;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function generateWitnessNames(ctx) {
|
||||
|
||||
const totals = {
|
||||
"output": 0,
|
||||
"pubInput": 0,
|
||||
"one": 0,
|
||||
"prvInput": 0,
|
||||
"internal": 0,
|
||||
"constant": 0,
|
||||
};
|
||||
const totals = {};
|
||||
totals[ctx.stONE] = 0;
|
||||
totals[ctx.stOUTPUT] = 0;
|
||||
totals[ctx.stPUBINPUT] = 0;
|
||||
totals[ctx.stPRVINPUT] = 0;
|
||||
totals[ctx.stINTERNAL] = 0;
|
||||
totals[ctx.stDISCARDED] = 0;
|
||||
totals[ctx.stCONSTANT] = 0;
|
||||
const ids = {};
|
||||
|
||||
const counted = {};
|
||||
|
||||
// First classify the signals
|
||||
for (let s in ctx.signals) {
|
||||
for (let s=0; s<ctx.signals.length; s++) {
|
||||
|
||||
if ((ctx.verbose)&&(s%10000 == 0)) console.log("generate witness (counting): ", s);
|
||||
|
||||
const signal = ctx.signals[s];
|
||||
let lSignal = signal;
|
||||
while (lSignal.equivalence) lSignal = ctx.signals[lSignal.equivalence];
|
||||
while (lSignal.e>=0) lSignal = ctx.signals[lSignal.e];
|
||||
|
||||
if (!counted[lSignal.fullName]) {
|
||||
counted[lSignal.fullName] = true;
|
||||
totals[lSignal.category] ++;
|
||||
if (!( lSignal.o & ctx.COUNTED) ) {
|
||||
lSignal.o |= ctx.COUNTED;
|
||||
totals[lSignal.c] ++;
|
||||
}
|
||||
}
|
||||
|
||||
ids["one"] = 0;
|
||||
ids["output"] = 1;
|
||||
ids["pubInput"] = ids["output"] + totals["output"];
|
||||
ids["prvInput"] = ids["pubInput"] + totals["pubInput"];
|
||||
ids["internal"] = ids["prvInput"] + totals["prvInput"];
|
||||
ids["constant"] = ids["internal"] + totals["internal"];
|
||||
const nSignals = ids["constant"] + totals["constant"];
|
||||
ids[ctx.stONE] = 0;
|
||||
ids[ctx.stOUTPUT] = 1;
|
||||
ids[ctx.stPUBINPUT] = ids[ctx.stOUTPUT] + totals[ctx.stOUTPUT];
|
||||
ids[ctx.stPRVINPUT] = ids[ctx.stPUBINPUT] + totals[ctx.stPUBINPUT];
|
||||
ids[ctx.stINTERNAL] = ids[ctx.stPRVINPUT] + totals[ctx.stPRVINPUT];
|
||||
ids[ctx.stDISCARDED] = ids[ctx.stINTERNAL] + totals[ctx.stINTERNAL];
|
||||
ids[ctx.stCONSTANT] = ids[ctx.stDISCARDED] + totals[ctx.stDISCARDED];
|
||||
const nSignals = ids[ctx.stCONSTANT] + totals[ctx.stCONSTANT];
|
||||
|
||||
ctx.signalNames = new Array(nSignals);
|
||||
for (let i=0; i< nSignals; i++) ctx.signalNames[i] = [];
|
||||
ctx.signalName2Idx = {};
|
||||
for (let s=0; s<ctx.signals.length; s++) {
|
||||
|
||||
if ((ctx.verbose)&&(s%10000 == 0)) console.log("seting id: ", s);
|
||||
|
||||
for (let s in ctx.signals) {
|
||||
const signal = ctx.signals[s];
|
||||
let lSignal = signal;
|
||||
while (lSignal.equivalence) {
|
||||
lSignal = ctx.signals[lSignal.equivalence];
|
||||
while (lSignal.e>=0) {
|
||||
lSignal = ctx.signals[lSignal.e];
|
||||
}
|
||||
if ( typeof(lSignal.id) === "undefined" ) {
|
||||
lSignal.id = ids[lSignal.category] ++;
|
||||
lSignal.id = ids[lSignal.c] ++;
|
||||
}
|
||||
|
||||
signal.id = lSignal.id;
|
||||
ctx.signalNames[signal.id].push(signal.fullName);
|
||||
ctx.signalName2Idx[signal.fullName] = signal.id;
|
||||
}
|
||||
|
||||
ctx.totals = totals;
|
||||
@@ -225,6 +233,7 @@ function reduceConstants(ctx) {
|
||||
if (!lc.isZero(c)) {
|
||||
newConstraints.push(c);
|
||||
}
|
||||
delete ctx.constraints[i];
|
||||
}
|
||||
ctx.constraints = newConstraints;
|
||||
}
|
||||
@@ -232,10 +241,12 @@ function reduceConstants(ctx) {
|
||||
function reduceConstrains(ctx) {
|
||||
indexVariables();
|
||||
let possibleConstraints = Object.keys(ctx.constraints);
|
||||
let ii=0;
|
||||
while (possibleConstraints.length>0) {
|
||||
let nextPossibleConstraints = {};
|
||||
for (let i in possibleConstraints) {
|
||||
if ((ctx.verbose)&&(i%10000 == 0)) console.log("reducing constraints: ", i);
|
||||
ii++;
|
||||
if ((ctx.verbose)&&(ii%10000 == 0)) console.log("reducing constraints: ", i);
|
||||
if (!ctx.constraints[i]) continue;
|
||||
const c = ctx.constraints[i];
|
||||
|
||||
@@ -248,13 +259,13 @@ function reduceConstrains(ctx) {
|
||||
|
||||
// Mov to C if possible.
|
||||
if (isConstant(c.a)) {
|
||||
const ct = {type: "NUMBER", value: c.a.values["one"]};
|
||||
const ct = {type: "NUMBER", value: c.a.values[sONE]};
|
||||
c.c = lc.add(lc.mul(c.b, ct), c.c);
|
||||
c.a = { type: "LINEARCOMBINATION", values: {} };
|
||||
c.b = { type: "LINEARCOMBINATION", values: {} };
|
||||
}
|
||||
if (isConstant(c.b)) {
|
||||
const ct = {type: "NUMBER", value: c.b.values["one"]};
|
||||
const ct = {type: "NUMBER", value: c.b.values[sONE]};
|
||||
c.c = lc.add(lc.mul(c.a, ct), c.c);
|
||||
c.a = { type: "LINEARCOMBINATION", values: {} };
|
||||
c.b = { type: "LINEARCOMBINATION", values: {} };
|
||||
@@ -265,8 +276,8 @@ function reduceConstrains(ctx) {
|
||||
if (isolatedSignal) {
|
||||
|
||||
let lSignal = ctx.signals[isolatedSignal];
|
||||
while (lSignal.equivalence) {
|
||||
lSignal = ctx.signals[lSignal.equivalence];
|
||||
while (lSignal.e>=0) {
|
||||
lSignal = ctx.signals[lSignal.e];
|
||||
}
|
||||
|
||||
|
||||
@@ -296,7 +307,7 @@ function reduceConstrains(ctx) {
|
||||
|
||||
ctx.constraints[i] = null;
|
||||
|
||||
lSignal.category = "constant";
|
||||
lSignal.c = ctx.stDISCARDED;
|
||||
} else {
|
||||
if (lc.isZero(c.c)) ctx.constraints[i] = null;
|
||||
}
|
||||
@@ -332,8 +343,8 @@ function reduceConstrains(ctx) {
|
||||
function unindexVariables() {
|
||||
for (let s in ctx.signals) {
|
||||
let lSignal = ctx.signals[s];
|
||||
while (lSignal.equivalence) {
|
||||
lSignal = ctx.signals[lSignal.equivalence];
|
||||
while (lSignal.e>=0) {
|
||||
lSignal = ctx.signals[lSignal.e];
|
||||
}
|
||||
if (lSignal.inConstraints) delete lSignal.inConstraints;
|
||||
}
|
||||
@@ -342,8 +353,8 @@ function reduceConstrains(ctx) {
|
||||
/*
|
||||
function unlinkSignal(signalName, cidx) {
|
||||
let lSignal = ctx.signals[signalName];
|
||||
while (lSignal.equivalence) {
|
||||
lSignal = ctx.signals[lSignal.equivalence];
|
||||
while (lSignal.e>=0) {
|
||||
lSignal = ctx.signals[lSignal.e];
|
||||
}
|
||||
if ((lSignal.inConstraints)&&(lSignal.inConstraints[cidx])) {
|
||||
delete lSignal.inConstraints[cidx];
|
||||
@@ -353,8 +364,8 @@ function reduceConstrains(ctx) {
|
||||
|
||||
function linkSignal(signalName, cidx) {
|
||||
let lSignal = ctx.signals[signalName];
|
||||
while (lSignal.equivalence) {
|
||||
lSignal = ctx.signals[lSignal.equivalence];
|
||||
while (lSignal.e>=0) {
|
||||
lSignal = ctx.signals[lSignal.e];
|
||||
}
|
||||
if (!lSignal.inConstraints) lSignal.inConstraints = {};
|
||||
lSignal.inConstraints[cidx] = true;
|
||||
@@ -363,21 +374,22 @@ function reduceConstrains(ctx) {
|
||||
function getFirstInternalSignal(ctx, l) {
|
||||
for (let k in l.values) {
|
||||
const signal = ctx.signals[k];
|
||||
if (signal.category == "internal") return k;
|
||||
if (signal.c == ctx.stINTERNAL) return k;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function isConstant(l) {
|
||||
for (let k in l.values) {
|
||||
if ((k != "one") && (!l.values[k].isZero())) return false;
|
||||
if ((k != sONE) && (!l.values[k].isZero())) return false;
|
||||
}
|
||||
if (!l.values["one"] || l.values["one"].isZero()) return false;
|
||||
if (!l.values[sONE] || l.values[sONE].isZero()) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
function buildCircuitDef(ctx, mainCode) {
|
||||
const res = {
|
||||
@@ -436,6 +448,9 @@ function buildCircuitDef(ctx, mainCode) {
|
||||
return res;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
Build constraints
|
||||
|
||||
@@ -485,5 +500,96 @@ function buildConstraints(ctx) {
|
||||
return res;
|
||||
}
|
||||
|
||||
function buildR1cs(ctx, strm) {
|
||||
|
||||
strm.write(Buffer.from([0x72,0x31,0x63,0x73]));
|
||||
writeU32(1);
|
||||
writeU32(4);
|
||||
writeU32(1 + ctx.totals.output + ctx.totals.pubInput + ctx.totals.prvInput + ctx.totals.internal);
|
||||
writeU32(ctx.totals.output);
|
||||
writeU32(ctx.totals.pubInput);
|
||||
writeU32(ctx.totals.prvInput);
|
||||
writeU32(ctx.constraints.length);
|
||||
|
||||
for (let i=0; i<ctx.constraints.length; i++) {
|
||||
if ((ctx.verbose)&&(i%10000 == 0)) console.log("writing constraint: ", i);
|
||||
writeConstraint(ctx.constraints[i]);
|
||||
}
|
||||
|
||||
function writeU32(v) {
|
||||
const b = Buffer.allocUnsafe(4);
|
||||
b.writeInt32LE(v);
|
||||
strm.write(b);
|
||||
}
|
||||
|
||||
function writeConstraint(c) {
|
||||
writeLC(c.a);
|
||||
writeLC(c.b);
|
||||
writeLC(lc.negate(c.c));
|
||||
}
|
||||
|
||||
function writeLC(lc) {
|
||||
const idxs = Object.keys(lc.values);
|
||||
writeU32(idxs.length);
|
||||
for (let s in lc.values) {
|
||||
let lSignal = ctx.signals[s];
|
||||
|
||||
while (lSignal.e >=0 ) lSignal = ctx.signals[lSignal.e];
|
||||
|
||||
writeU32(lSignal.id);
|
||||
writeBigInt(lc.values[s]);
|
||||
}
|
||||
}
|
||||
|
||||
function writeBigInt(n) {
|
||||
const bytes = [];
|
||||
let r = bigInt(n);
|
||||
while (r.greater(bigInt.zero)) {
|
||||
bytes.push(r.and(bigInt("255")).toJSNumber());
|
||||
r = r.shiftRight(8);
|
||||
}
|
||||
assert(bytes.length<=32);
|
||||
assert(bytes.length>0);
|
||||
strm.write( Buffer.from([bytes.length, ...bytes ]));
|
||||
}
|
||||
}
|
||||
|
||||
function buildSyms(ctx, strm) {
|
||||
|
||||
|
||||
|
||||
addSymbolsComponent(ctx.mainComponent + ".", ctx.getComponentIdx(ctx.mainComponent));
|
||||
|
||||
|
||||
function addSymbolsComponent(prefix, idComponet) {
|
||||
for (let n in ctx.components[idComponet].names.o) {
|
||||
const entrie = ctx.components[idComponet].names.o[n];
|
||||
addSymbolArray(prefix+n, entrie.type, entrie.sizes, entrie.offset);
|
||||
}
|
||||
}
|
||||
|
||||
function addSymbolArray(prefix, type, sizes, offset) {
|
||||
if (sizes.length==0) {
|
||||
if (type == "S") {
|
||||
let s=offset;
|
||||
while (ctx.signals[s].e >= 0) s = ctx.signals[s].e;
|
||||
let wId = ctx.signals[s].id;
|
||||
if (typeof(wId) == "undefined") wId=-1;
|
||||
strm.write(`${offset},${wId},${prefix}\n`);
|
||||
} else {
|
||||
addSymbolsComponent(prefix+".", offset);
|
||||
}
|
||||
return 1;
|
||||
} else {
|
||||
let acc = 0;
|
||||
for (let i=0; i<sizes[0]; i++) {
|
||||
acc += addSymbolArray(`${prefix}[${i}]`, type, sizes.slice(1), offset + acc );
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
171
src/ctx.js
Normal file
171
src/ctx.js
Normal file
@@ -0,0 +1,171 @@
|
||||
const bigInt = require("big-integer");
|
||||
|
||||
|
||||
class TableName {
|
||||
constructor (ctx) {
|
||||
this.ctx = ctx;
|
||||
this.o = {};
|
||||
}
|
||||
|
||||
_allocElement(name, _sizes, type) {
|
||||
const sizes = _sizes || [];
|
||||
let l = 1;
|
||||
for (let i=0; i<sizes.length; i++) {
|
||||
l = l*sizes[i];
|
||||
}
|
||||
this.o[name] = {
|
||||
sizes: sizes,
|
||||
type: type
|
||||
};
|
||||
return l;
|
||||
}
|
||||
|
||||
addSignal(name, sizes) {
|
||||
const l = this._allocElement(name, sizes, "S");
|
||||
const o = this.ctx.nSignals;
|
||||
this.o[name].offset = o;
|
||||
this.ctx.nSignals += l;
|
||||
if (l>1) {
|
||||
return [o, o+l];
|
||||
} else {
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
addComponent(name, sizes) {
|
||||
const l = this._allocElement(name, sizes, "C");
|
||||
const o = this.ctx.nComponents;
|
||||
this.o[name].offset = o;
|
||||
this.ctx.nComponents += l;
|
||||
if (l>1) {
|
||||
return [o, o+l];
|
||||
} else {
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
_getElement(name, _sels, type) {
|
||||
const sels = _sels || [];
|
||||
const s = this.o[name];
|
||||
if (!s) return -1;
|
||||
if (s.type != type) return -1;
|
||||
if (sels.length > s.sizes.length) return -1;
|
||||
let l=1;
|
||||
for (let i = s.sizes.length-1; i>sels.length; i--) {
|
||||
l = l*s.sizes[i];
|
||||
}
|
||||
let o =0;
|
||||
let p=1;
|
||||
for (let i=sels.length-1; i>=0; i--) {
|
||||
if (sels[i] > s.sizes[i]) return -1; // Out of range
|
||||
if (sels[i] < 0) return -1; // Out of range
|
||||
o += p*sels[i];
|
||||
p *= s.sizes[i];
|
||||
}
|
||||
if (l>1) {
|
||||
return [s.offset + o, s.offset + o + l];
|
||||
} else {
|
||||
return s.offset + o;
|
||||
}
|
||||
}
|
||||
|
||||
getSignalIdx(name, sels) {
|
||||
return this._getElement(name, sels, "S");
|
||||
}
|
||||
|
||||
getComponentIdx(name, sels) {
|
||||
return this._getElement(name, sels, "C");
|
||||
}
|
||||
|
||||
getSizes(name) {
|
||||
return this.o[name].sels;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = class Ctx {
|
||||
|
||||
constructor() {
|
||||
|
||||
this.stONE = 1;
|
||||
this.stOUTPUT = 2;
|
||||
this.stPUBINPUT = 3;
|
||||
this.stPRVINPUT = 4;
|
||||
this.stINTERNAL = 5;
|
||||
this.stDISCARDED = 6;
|
||||
this.stCONSTANT = 7;
|
||||
|
||||
this.IN = 0x01;
|
||||
this.OUT = 0x02;
|
||||
this.PRV = 0x04;
|
||||
this.ONE = 0x08;
|
||||
this.MAIN = 0x10;
|
||||
this.COUNTED = 0x20;
|
||||
|
||||
this.scopes = [{}];
|
||||
this.signals = [];
|
||||
|
||||
this.currentComponent= -1;
|
||||
this.constraints= [];
|
||||
this.components= [];
|
||||
this.templates= {};
|
||||
this.functions= {};
|
||||
this.functionParams= {};
|
||||
this.nSignals = 0;
|
||||
this.nComponents =0;
|
||||
this.names = new TableName(this);
|
||||
this.main=false;
|
||||
|
||||
const oneIdx = this.addSignal("one");
|
||||
this.signals[oneIdx] = {
|
||||
v: bigInt(1),
|
||||
o: this.ONE,
|
||||
e: -1,
|
||||
};
|
||||
}
|
||||
|
||||
addSignal(name, sizes) {
|
||||
if (this.currentComponent>=0) {
|
||||
return this.components[this.currentComponent].names.addSignal(name, sizes);
|
||||
} else {
|
||||
return this.names.addSignal(name, sizes);
|
||||
}
|
||||
}
|
||||
|
||||
addComponent(name, sizes) {
|
||||
if (this.currentComponent>=0) {
|
||||
return this.components[this.currentComponent].names.addComponent(name, sizes);
|
||||
} else {
|
||||
return this.names.addComponent(name, sizes);
|
||||
}
|
||||
}
|
||||
|
||||
getSignalIdx(name, sels) {
|
||||
if (this.currentComponent>=0) {
|
||||
return this.components[this.currentComponent].names.getSignalIdx(name, sels);
|
||||
} else {
|
||||
return this.names.getSignalIdx(name, sels);
|
||||
}
|
||||
}
|
||||
|
||||
getComponentIdx(name, sels) {
|
||||
if (this.currentComponent>=0) {
|
||||
return this.components[this.currentComponent].names.getComponentIdx(name, sels);
|
||||
} else {
|
||||
return this.names.getComponentIdx(name, sels);
|
||||
}
|
||||
}
|
||||
|
||||
getSizes(name) {
|
||||
if (this.currentComponent>=0) {
|
||||
return this.components[this.currentComponent].names.getSizes(name);
|
||||
} else {
|
||||
return this.names.getSizes(name);
|
||||
}
|
||||
}
|
||||
|
||||
newTableName() {
|
||||
return new TableName(this);
|
||||
}
|
||||
|
||||
};
|
||||
227
src/exec.js
227
src/exec.js
@@ -214,16 +214,38 @@ function getScope(ctx, name, selectors) {
|
||||
}
|
||||
|
||||
|
||||
function select(v, s) {
|
||||
s = s || [];
|
||||
if (s.length == 0) return v;
|
||||
return select(v[s[0]], s.slice(1));
|
||||
function select(v, sels) {
|
||||
if (v.type == "SIGNAL") {
|
||||
return reduce(v, sels, "sIdx");
|
||||
} else if (v.type == "COMPONENT") {
|
||||
return reduce(v, sels, "cIdx");
|
||||
} else {
|
||||
const s = sels || [];
|
||||
if (s.length == 0) return v;
|
||||
return select(v[s[0]], s.slice(1));
|
||||
}
|
||||
}
|
||||
|
||||
for (let i=ctx.scopes.length-1; 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<sels.length; i++) {
|
||||
res[idxName] += sels[i]*accSizes[i];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
function getScopeLevel(ctx, name) {
|
||||
@@ -255,6 +277,10 @@ function execTemplateDef(ctx, ast) {
|
||||
filePath: ctx.filePath,
|
||||
scopes: copyScope(ctx.scopes)
|
||||
};
|
||||
ctx.templates[ast.name] = {
|
||||
block: ast.block,
|
||||
params: ast.params
|
||||
};
|
||||
}
|
||||
|
||||
function execFunctionDef(ctx, ast) {
|
||||
@@ -272,6 +298,10 @@ function execFunctionDef(ctx, ast) {
|
||||
filePath: ctx.filePath,
|
||||
scopes: copyScope(ctx.scopes)
|
||||
};
|
||||
ctx.functions[ast.name] = {
|
||||
block: ast.block,
|
||||
params: ast.params
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -281,8 +311,6 @@ function execDeclareComponent(ctx, ast) {
|
||||
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 baseName = ctx.currentComponent ? ctx.currentComponent + "." + ast.name.name : ast.name.name;
|
||||
|
||||
const sizes=[];
|
||||
for (let i=0; i< ast.name.selectors.length; i++) {
|
||||
const size = exec(ctx, ast.name.selectors[i]);
|
||||
@@ -293,16 +321,13 @@ function execDeclareComponent(ctx, ast) {
|
||||
sizes.push( size.value.toJSNumber() );
|
||||
}
|
||||
|
||||
const cIdx = ctx.addComponent(ast.name.name, sizes);
|
||||
|
||||
scope[ast.name.name] = iterateSelectors(ctx, sizes, baseName, function(fullName) {
|
||||
|
||||
ctx.components[fullName] = "UNINSTANTIATED";
|
||||
|
||||
return {
|
||||
type: "COMPONENT",
|
||||
fullName: fullName
|
||||
};
|
||||
});
|
||||
scope[ast.name.name] = {
|
||||
type: "COMPONENT",
|
||||
sizes: sizes,
|
||||
cIdx: Array.isArray(cIdx) ? cIdx[0] : cIdx
|
||||
};
|
||||
|
||||
return {
|
||||
type: "VARIABLE",
|
||||
@@ -333,15 +358,20 @@ function execInstantiateComponet(ctx, vr, fn) {
|
||||
|
||||
paramValues.push(v);
|
||||
}
|
||||
if (template.params.length != paramValues.length) error(ctx, fn, "Invalid Number of parameters");
|
||||
if (template.params.length != paramValues.length) return error(ctx, fn, "Invalid Number of parameters");
|
||||
|
||||
const vv = getScope(ctx, componentName, vr.selectors);
|
||||
|
||||
if (!vv) return error(ctx, vr, "Component not defined"+ componentName);
|
||||
const vComp = getScope(ctx, componentName, vr.selectors);
|
||||
if (vComp.type != "COMPONENT") return error(ctx, fn, "Assigning to a non component");
|
||||
const cIdx = vComp.cIdx;
|
||||
if (cIdx == -1) return error(ctx, fn, "Component not defined");
|
||||
let l=1;
|
||||
for (let i=0; i<vComp.sizes.length; i++) l = l*vComp.sizes[i];
|
||||
for (let i=0; i<l; i++) {
|
||||
instantiateComponent(cIdx+i);
|
||||
}
|
||||
|
||||
instantiateComponent(vv);
|
||||
|
||||
function instantiateComponent(varVal) {
|
||||
function instantiateComponent(cIdx) {
|
||||
|
||||
function extractValue(v) {
|
||||
if (Array.isArray(v)) {
|
||||
@@ -351,23 +381,25 @@ function execInstantiateComponet(ctx, vr, fn) {
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(varVal)) {
|
||||
for (let i =0; i<varVal.length; i++) {
|
||||
instantiateComponent(varVal[i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx.components[varVal.fullName] != "UNINSTANTIATED") error(ctx, fn, "Component already instantiated");
|
||||
if (ctx.components[cIdx]) return error(ctx, fn, "Component already instantiated");
|
||||
|
||||
const oldComponent = ctx.currentComponent;
|
||||
const oldFileName = ctx.fileName;
|
||||
const oldFilePath = ctx.filePath;
|
||||
ctx.currentComponent = varVal.fullName;
|
||||
const oldMain = ctx.main;
|
||||
|
||||
ctx.components[ctx.currentComponent] = {
|
||||
signals: [],
|
||||
params: {}
|
||||
if ((componentName == "main")&&(ctx.currentComponent==-1)) {
|
||||
ctx.main=true;
|
||||
} else {
|
||||
ctx.main=false;
|
||||
}
|
||||
|
||||
ctx.currentComponent = cIdx;
|
||||
|
||||
ctx.components[cIdx] = {
|
||||
params: {},
|
||||
names: ctx.newTableName(),
|
||||
nInSignals: 0
|
||||
};
|
||||
|
||||
const oldScopes = ctx.scopes;
|
||||
@@ -379,20 +411,22 @@ function execInstantiateComponet(ctx, vr, fn) {
|
||||
const scope = {};
|
||||
for (let i=0; i< template.params.length; i++) {
|
||||
scope[template.params[i]] = paramValues[i];
|
||||
ctx.components[ctx.currentComponent].params[template.params[i]] = extractValue(paramValues[i]);
|
||||
ctx.components[cIdx].params[template.params[i]] = extractValue(paramValues[i]);
|
||||
}
|
||||
|
||||
ctx.components[ctx.currentComponent].template = templateName;
|
||||
ctx.components[cIdx].template = templateName;
|
||||
ctx.fileName = template.fileName;
|
||||
ctx.filePath = template.filePath;
|
||||
ctx.scopes = copyScope( template.scopes );
|
||||
ctx.scopes.push(scope);
|
||||
|
||||
|
||||
execBlock(ctx, template.block);
|
||||
|
||||
ctx.fileName = oldFileName;
|
||||
ctx.filePath = oldFilePath;
|
||||
ctx.currentComponent = oldComponent;
|
||||
ctx.main = oldMain;
|
||||
ctx.scopes = oldScopes;
|
||||
}
|
||||
}
|
||||
@@ -461,32 +495,47 @@ function execDeclareSignal(ctx, ast) {
|
||||
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 baseName = ctx.currentComponent ? ctx.currentComponent + "." + ast.name.name : ast.name.name;
|
||||
|
||||
const sizes=[];
|
||||
let totalSize=1;
|
||||
for (let i=0; i< ast.name.selectors.length; i++) {
|
||||
const size = exec(ctx, ast.name.selectors[i]);
|
||||
if (ctx.error) return;
|
||||
|
||||
if (size.type != "NUMBER") return error(ctx, ast.name.selectors[i], "expected a number");
|
||||
sizes.push( size.value.toJSNumber() );
|
||||
const s = size.value.toJSNumber();
|
||||
totalSize *= s;
|
||||
sizes.push( s );
|
||||
}
|
||||
|
||||
scope[ast.name.name] = iterateSelectors(ctx, sizes, baseName, function(fullName) {
|
||||
ctx.signals[fullName] = {
|
||||
fullName: fullName,
|
||||
direction: ast.declareType == "SIGNALIN" ? "IN" : (ast.declareType == "SIGNALOUT" ? "OUT" : ""),
|
||||
private: ast.private,
|
||||
component: ctx.currentComponent,
|
||||
equivalence: "",
|
||||
alias: [fullName]
|
||||
let sIdx = ctx.addSignal(ast.name.name, sizes);
|
||||
if (!Array.isArray(sIdx)) sIdx = [sIdx, sIdx+1];
|
||||
for (let i=sIdx[0]; i<sIdx[1]; i++) {
|
||||
ctx.signals[i] = {
|
||||
o: 0,
|
||||
e: -1
|
||||
};
|
||||
ctx.components[ctx.currentComponent].signals.push(fullName);
|
||||
return {
|
||||
type: "SIGNAL",
|
||||
fullName: fullName,
|
||||
};
|
||||
});
|
||||
|
||||
if (ast.declareType == "SIGNALIN") {
|
||||
ctx.signals[i].o |= ctx.IN;
|
||||
ctx.components[ctx.currentComponent].nInSignals+=1;
|
||||
}
|
||||
if (ast.declareType == "SIGNALOUT") {
|
||||
ctx.signals[i].o |= ctx.OUT;
|
||||
}
|
||||
if (ast.private ) {
|
||||
ctx.signals[i].o |= ctx.PRV;
|
||||
}
|
||||
if (ctx.main) {
|
||||
ctx.signals[i].o |= ctx.MAIN;
|
||||
}
|
||||
|
||||
// ctx.components[ctx.currentComponent].signals.push(i);
|
||||
}
|
||||
scope[ast.name.name] = {
|
||||
type: "SIGNAL",
|
||||
sizes: sizes,
|
||||
sIdx: sIdx[0]
|
||||
};
|
||||
return {
|
||||
type: "VARIABLE",
|
||||
name: ast.name.name,
|
||||
@@ -535,10 +584,10 @@ function execVariable(ctx, ast) {
|
||||
if (!v) return error(ctx, ast, "Variable not defined");
|
||||
|
||||
// If the signal has an assigned value (constant) just return the constant
|
||||
if ((v.type == "SIGNAL") && (ctx.signals[v.fullName].value)) {
|
||||
if ((v.type == "SIGNAL") && (ctx.signals[v.sIdx].value)) {
|
||||
return {
|
||||
type: "NUMBER",
|
||||
value: ctx.signals[v.fullName].value
|
||||
value: ctx.signals[v.sIdx].value
|
||||
};
|
||||
}
|
||||
let res;
|
||||
@@ -547,23 +596,43 @@ function execVariable(ctx, ast) {
|
||||
}
|
||||
|
||||
function execPin(ctx, ast) {
|
||||
const component = getScope(ctx, ast.component.name, ast.component.selectors);
|
||||
if (!component) return error(ctx, ast.component, "Component does not exists: "+ast.component.name);
|
||||
if (ctx.error) return;
|
||||
let signalFullName = component.fullName + "." + ast.pin.name;
|
||||
const selsC = [];
|
||||
for (let i=0; i< ast.component.selectors.length; i++) {
|
||||
const sel = exec(ctx, ast.component.selectors[i]);
|
||||
if (sel.type != "NUMBER") return error(ctx, ast.pin.selectors[i], "expected a number");
|
||||
selsC.push(sel.value.toJSNumber());
|
||||
}
|
||||
|
||||
const cIdx = ctx.getComponentIdx(ast.component.name, selsC);
|
||||
if (cIdx<0) return error(ctx, ast.component, "Component does not exists: "+ast.component.name);
|
||||
|
||||
const selsP = [];
|
||||
for (let i=0; i< ast.pin.selectors.length; i++) {
|
||||
const sel = exec(ctx, ast.pin.selectors[i]);
|
||||
if (ctx.error) return;
|
||||
|
||||
if (sel.type != "NUMBER") return error(ctx, ast.pin.selectors[i], "expected a number");
|
||||
|
||||
signalFullName += "[" + sel.value.toJSNumber() + "]";
|
||||
selsP.push(sel.value.toJSNumber());
|
||||
}
|
||||
if (!ctx.signals[signalFullName]) error(ctx, ast, "Signal not defined:" + signalFullName);
|
||||
const sIdx = ctx.components[cIdx].names.getSignalIdx(ast.pin.name, selsP);
|
||||
|
||||
if (sIdx<0) error(ctx, ast, "Signal not defined:" + buildFullName() );
|
||||
return {
|
||||
type: "SIGNAL",
|
||||
fullName: signalFullName
|
||||
sIdx: sIdx
|
||||
};
|
||||
|
||||
function buildFullName() {
|
||||
return ast.component.name + sels2str(selsC) + "." + ast.pin.name + sels2str(selsP);
|
||||
}
|
||||
|
||||
function sels2str(sels) {
|
||||
let S = "";
|
||||
for (let i=0; i< sels.length; i++) {
|
||||
const sel = exec(ctx, ast.pin.selectors[i]);
|
||||
if (sel.type != "NUMBER") return error(ctx, ast.pin.selectors[i], "expected a number");
|
||||
S += "[" + sel.value.toString() + "]";
|
||||
}
|
||||
return S;
|
||||
}
|
||||
}
|
||||
|
||||
function execFor(ctx, ast) {
|
||||
@@ -994,13 +1063,13 @@ function execSignalAssign(ctx, ast) {
|
||||
if (!dst) return error(ctx, ast, "Signal not defined");
|
||||
if (dst.type != "SIGNAL") return error(ctx, ast, "Signal assigned to a non signal");
|
||||
|
||||
let sDest=ctx.signals[dst.fullName];
|
||||
if (!sDest) return error(ctx, ast, "Invalid signal: "+dst.fullName);
|
||||
let sDest=ctx.signals[dst.sIdx];
|
||||
if (!sDest) return error(ctx, ast, "Invalid signal: "+dst.sIdx);
|
||||
|
||||
let isOut = (sDest.component == "main")&&(sDest.direction=="OUT");
|
||||
while (sDest.equivalence) {
|
||||
sDest=ctx.signals[sDest.equivalence];
|
||||
isOut = isOut || ((sDest.component == "main")&&(sDest.direction=="OUT"));
|
||||
let isOut = (sDest.o & ctx.MAIN)&&(sDest.o & ctx.OUT);
|
||||
while (sDest.e>=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");
|
||||
@@ -1029,18 +1098,17 @@ function execSignalAssign(ctx, ast) {
|
||||
|
||||
let assignValue = true;
|
||||
if (src.type == "SIGNAL") {
|
||||
let sSrc = ctx.signals[src.fullName];
|
||||
let isIn = (sSrc.component == "main")&&(sSrc.direction == "IN");
|
||||
while (sSrc.equivalence) {
|
||||
sSrc=ctx.signals[sSrc.equivalence];
|
||||
isIn = isIn || ((sSrc.component == "main")&&(sSrc.direction == "IN"));
|
||||
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.equivalence = src.fullName;
|
||||
sDest.alias = sDest.alias.concat(src.alias);
|
||||
while (sDest.equivalence) sDest=ctx.signals[sDest.equivalence];
|
||||
sDest.e = src.sIdx;
|
||||
while (sDest.e >= 0) sDest=ctx.signals[sDest.e];
|
||||
assignValue = false;
|
||||
}
|
||||
}
|
||||
@@ -1095,10 +1163,11 @@ function execInclude(ctx, ast) {
|
||||
ctx.includedFiles = ctx.includedFiles || [];
|
||||
if (ctx.includedFiles[incFileName]) return;
|
||||
|
||||
ctx.includedFiles[incFileName] = true;
|
||||
|
||||
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);
|
||||
|
||||
@@ -188,26 +188,28 @@ function genBlock(ctx, ast) {
|
||||
}
|
||||
|
||||
function genTemplateDef(ctx, ast) {
|
||||
let S = "function(ctx) ";
|
||||
|
||||
const newScope = {};
|
||||
if (ctx.f) return error(ctx, ast, "Already in function");
|
||||
|
||||
ctx.f = ctx.module.addFunction(ast.name);
|
||||
ctx.c = ctx.f.getCodeBuilder();
|
||||
|
||||
ctx.scope = {};
|
||||
for (let i=0; i< ast.params.length; i++) {
|
||||
newScope[ast.params[i]] = { type: "VARIABLE" };
|
||||
ctx.f.addParam(ast.params[i].name, "i32");
|
||||
ctx.scope[ast.params[i].name] = {
|
||||
type: "PARAM",
|
||||
sels: ast.params[i].sels,
|
||||
getter: () => { return ctx.c.getLocal(ast.params[i].name); },
|
||||
setter: (v) => { return ctx.c.setLocal(ast.params[i].name, v); }
|
||||
};
|
||||
}
|
||||
|
||||
ctx.scopes.push(newScope);
|
||||
S += genBlock(ctx, ast.block);
|
||||
ctx.scopes.pop();
|
||||
genBlock(ctx, ast.block);
|
||||
|
||||
// const scope = ctx.scopes[ctx.scopes.length-1];
|
||||
const scope = ctx.scopes[0]; // Scope for templates is top
|
||||
|
||||
scope[ast.name] = {
|
||||
type: "TEMPLATE"
|
||||
};
|
||||
|
||||
ctx.templates[ast.name] = S;
|
||||
return "";
|
||||
ctx.scope = null;
|
||||
ctx.c = null;
|
||||
ctx.f = null;
|
||||
}
|
||||
|
||||
function genFunctionDef(ctx, ast) {
|
||||
|
||||
@@ -60,6 +60,7 @@ QEQ QEQ ERR ERR
|
||||
|
||||
const bigInt = require("big-integer");
|
||||
const __P__ = new bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
||||
const sONE = 0;
|
||||
|
||||
exports.add = add;
|
||||
exports.mul = mul;
|
||||
@@ -79,7 +80,7 @@ function signal2lc(a) {
|
||||
type: "LINEARCOMBINATION",
|
||||
values: {}
|
||||
};
|
||||
lc.values[a.fullName] = bigInt(1);
|
||||
lc.values[a.sIdx] = bigInt(1);
|
||||
return lc;
|
||||
} else {
|
||||
return a;
|
||||
@@ -163,10 +164,10 @@ function addLCNum(a,b) {
|
||||
return { type: "ERROR", errStr: "LinearCombination + undefined" };
|
||||
}
|
||||
if (b.value.isZero()) return res;
|
||||
if (!res.values["one"]) {
|
||||
res.values["one"]=bigInt(b.value);
|
||||
if (!res.values[sONE]) {
|
||||
res.values[sONE]=bigInt(b.value);
|
||||
} else {
|
||||
res.values["one"]= res.values["one"].add(b.value).mod(__P__);
|
||||
res.values[sONE]= res.values[sONE].add(b.value).mod(__P__);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@@ -278,16 +279,16 @@ function mulQEQNum(a,b) {
|
||||
return res;
|
||||
}
|
||||
|
||||
function getSignalValue(ctx, signalName) {
|
||||
const s = ctx.signals[signalName];
|
||||
if (s.equivalence != "") {
|
||||
return getSignalValue(ctx, s.equivalence);
|
||||
function getSignalValue(ctx, sIdx) {
|
||||
const s = ctx.signals[sIdx];
|
||||
if (s.e >= 0) {
|
||||
return getSignalValue(ctx, s.e);
|
||||
} else {
|
||||
const res = {
|
||||
type: "NUMBER"
|
||||
};
|
||||
if (s.value) {
|
||||
res.value = s.value;
|
||||
if (s.v) {
|
||||
res.value = s.v;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@@ -297,7 +298,7 @@ function evaluate(ctx, n) {
|
||||
if (n.type == "NUMBER") {
|
||||
return n;
|
||||
} else if (n.type == "SIGNAL") {
|
||||
return getSignalValue(ctx, n.fullName);
|
||||
return getSignalValue(ctx, n.sIdx);
|
||||
} else if (n.type == "LINEARCOMBINATION") {
|
||||
const v= {
|
||||
type: "NUMBER",
|
||||
@@ -362,7 +363,7 @@ function toQEQ(a) {
|
||||
type: "QEQ",
|
||||
a: {type: "LINEARCOMBINATION", values: {}},
|
||||
b: {type: "LINEARCOMBINATION", values: {}},
|
||||
c: {type: "LINEARCOMBINATION", values: {"one": bigInt(a.value)}}
|
||||
c: {type: "LINEARCOMBINATION", values: {sONE: bigInt(a.value)}}
|
||||
};
|
||||
} else if (a.type == "LINEARCOMBINATION") {
|
||||
return {
|
||||
@@ -415,11 +416,11 @@ function toString(a, ctx) {
|
||||
if (!c.equals(1)) {
|
||||
S = S + c.toString() + "*";
|
||||
}
|
||||
let sigName = k;
|
||||
let sIdx = k;
|
||||
if (ctx) {
|
||||
while (ctx.signals[sigName].equivalence) sigName = ctx.signals[sigName].equivalence;
|
||||
while (ctx.signals[sIdx].e>=0) sIdx = ctx.signals[sIdx].e;
|
||||
}
|
||||
S = S + sigName;
|
||||
S = S + "[" + sIdx + "]";
|
||||
}
|
||||
}
|
||||
if (S=="") return "0"; else return S;
|
||||
@@ -437,13 +438,13 @@ function canonize(ctx, a) {
|
||||
const res = clone(a);
|
||||
for (let k in a.values) {
|
||||
let s = k;
|
||||
while (ctx.signals[s].equivalence) s= ctx.signals[s].equivalence;
|
||||
if ((typeof(ctx.signals[s].value) != "undefined")&&(k != "one")) {
|
||||
while (ctx.signals[s].e>=0) s= ctx.signals[s].e;
|
||||
if ((typeof(ctx.signals[s].value) != "undefined")&&(k != sONE)) {
|
||||
const v = res.values[k].times(ctx.signals[s].value).mod(__P__);
|
||||
if (!res.values["one"]) {
|
||||
res.values["one"]=v;
|
||||
if (!res.values[sONE]) {
|
||||
res.values[sONE]=v;
|
||||
} else {
|
||||
res.values["one"]= res.values["one"].add(v).mod(__P__);
|
||||
res.values[sONE]= res.values[sONE].add(v).mod(__P__);
|
||||
}
|
||||
delete res.values[k];
|
||||
} else if (s != k) {
|
||||
|
||||
55
src/utils.js
Normal file
55
src/utils.js
Normal file
@@ -0,0 +1,55 @@
|
||||
const fnv = require("fnv-plus");
|
||||
|
||||
module.exports.ident =ident;
|
||||
|
||||
module.exports.extractSizes =extractSizes;
|
||||
module.exports.csArr = csArr;
|
||||
module.exports.subArray = subArray;
|
||||
module.exports.accSizes = accSizes;
|
||||
module.exports.fnvHash = fnvHash;
|
||||
|
||||
function ident(text) {
|
||||
let lines = text.split("\n");
|
||||
for (let i=0; i<lines.length; i++) {
|
||||
if (lines[i]) lines[i] = " "+lines[i];
|
||||
}
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
function extractSizes (o) {
|
||||
if (! Array.isArray(o)) return [1, 0];
|
||||
return [o.length, ...extractSizes(o[0])];
|
||||
}
|
||||
|
||||
// Input [1,2,3]
|
||||
// Returns " ,1 ,2, 3"
|
||||
function csArr(_arr) {
|
||||
let S = "";
|
||||
const arr = _arr || [];
|
||||
for (let i=0; i<arr.length; i++) {
|
||||
S = " ,"+arr[i];
|
||||
}
|
||||
return S;
|
||||
}
|
||||
|
||||
function subArray(value, sels) {
|
||||
if ((!sels) || (sels.length == 0)) return value;
|
||||
|
||||
return subArray(value[sels[0]], sels.slice(1));
|
||||
}
|
||||
|
||||
function accSizes(_sizes) {
|
||||
const sizes = _sizes || [];
|
||||
const accSizes = [1, 0];
|
||||
for (let i=sizes.length-1; i>=0; i--) {
|
||||
accSizes.unshift(accSizes[0]*sizes[i]);
|
||||
}
|
||||
return accSizes;
|
||||
}
|
||||
|
||||
function fnvHash(str) {
|
||||
return fnv.hash(str, 64).hex();
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user