mirror of
https://github.com/arnaucube/circom.git
synced 2026-02-07 03:06:42 +01:00
First commit
This commit is contained in:
209
src/calculateWitness.js
Normal file
209
src/calculateWitness.js
Normal file
@@ -0,0 +1,209 @@
|
||||
const bigInt = require("big-integer");
|
||||
|
||||
module.exports = calculateWitness;
|
||||
|
||||
function calculateWitness(circuit, inputSignals) {
|
||||
const ctx = new RTCtx(circuit);
|
||||
|
||||
function iterateSelector(values, sels, cb) {
|
||||
if (!Array.isArray(values)) {
|
||||
return cb(sels, values);
|
||||
}
|
||||
for (let i=0; i<values.length; i++) {
|
||||
sels.push(i);
|
||||
iterateSelector(values[i], sels, cb);
|
||||
sels.pop(i);
|
||||
}
|
||||
}
|
||||
|
||||
ctx.setSignal("one", [], bigInt(1));
|
||||
|
||||
for (let c in ctx.notInitSignals) {
|
||||
if (ctx.notInitSignals[c] == 0) ctx.triggerComponent(c);
|
||||
}
|
||||
|
||||
for (let s in inputSignals) {
|
||||
ctx.currentComponent = "main";
|
||||
iterateSelector(inputSignals[s], [], function(selector, value) {
|
||||
ctx.setSignal(s, selector, bigInt(value));
|
||||
});
|
||||
}
|
||||
|
||||
for (let i=0; i<ctx.witness.length; i++) {
|
||||
if (typeof(ctx.witness[i]) == "undefined") {
|
||||
throw("Signal not assigned: " + ctx.circuit.witnessNames[i].join(", "));
|
||||
}
|
||||
console.log(ctx.circuit.witnessNames[i].join(",") + " --> " + ctx.witness[i].toString());
|
||||
}
|
||||
return ctx.witness;
|
||||
}
|
||||
|
||||
class RTCtx {
|
||||
constructor(circuit) {
|
||||
this.scopes = [];
|
||||
this.circuit = circuit;
|
||||
this.witness = [];
|
||||
this.notInitSignals = {};
|
||||
for (let c in this.circuit.components) {
|
||||
this.notInitSignals[c] = this.circuit.components[c].inputSignals;
|
||||
if (this.notInitSignals == 0) {
|
||||
this.currentComponent = c;
|
||||
this.components.calc(this);
|
||||
this.currentComponent = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_sels2str(sels) {
|
||||
let res = "";
|
||||
for (let i=0; i<sels.length; i++) {
|
||||
res += `[${sels[i]}]`;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
setPin(componentName, componentSels, signalName, signalSels, value) {
|
||||
let fullName = componentName=="one" ? "one" : this.currentComponent + "." + componentName;
|
||||
fullName += this._sels2str(componentSels) +
|
||||
"."+
|
||||
signalName+
|
||||
this._sels2str(signalSels);
|
||||
this.setSignalFullName(fullName, value);
|
||||
}
|
||||
|
||||
setSignal(name, sels, value) {
|
||||
let fullName = this.currentComponent ? this.currentComponent + "." + name : name;
|
||||
fullName += this._sels2str(sels);
|
||||
this.setSignalFullName(fullName, value);
|
||||
}
|
||||
|
||||
triggerComponent(c) {
|
||||
console.log("Component Treiggered: " + c);
|
||||
|
||||
// Set notInitSignals to -1 to not initialize again
|
||||
this.notInitSignals[c] --;
|
||||
const oldComponent = this.currentComponent;
|
||||
this.currentComponent = c;
|
||||
const template = this.circuit.components[c].template;
|
||||
|
||||
const newScope = {};
|
||||
for (let p in this.circuit.components[c].params) {
|
||||
newScope[p] = this.circuit.components[c].params[p];
|
||||
}
|
||||
|
||||
const oldScope = this.scopes;
|
||||
this.scopes = [ this.scopes[0], newScope ];
|
||||
|
||||
// TODO set params.
|
||||
|
||||
this.circuit.templates[template](this);
|
||||
this.scopes = oldScope;
|
||||
this.currentComponent = oldComponent;
|
||||
}
|
||||
|
||||
callFunction(functionName, params) {
|
||||
|
||||
const newScope = {};
|
||||
for (let p=0; p<this.circuit.functionParams[functionName].length; p++) {
|
||||
const paramName = this.circuit.functionParams[functionName][p];
|
||||
newScope[paramName] = params[p];
|
||||
}
|
||||
|
||||
const oldScope = this.scopes;
|
||||
this.scopes = [ this.scopes[0], newScope ];
|
||||
|
||||
// TODO set params.
|
||||
|
||||
const res = this.circuit.functions[functionName](this);
|
||||
this.scopes = oldScope;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
setSignalFullName(fullName, value) {
|
||||
console.log("set " + fullName + " <-- " + value.toString());
|
||||
const sId = this.circuit.signals[fullName].id;
|
||||
let firstInit =false;
|
||||
if (!this.witness[sId]) {
|
||||
firstInit = true;
|
||||
}
|
||||
this.witness[sId] = value;
|
||||
const callComponents = [];
|
||||
for (let i=0; i<this.circuit.witnessNames[sId].length; i++) {
|
||||
const ss = this.circuit.witnessNames[sId][i];
|
||||
if (this.circuit.signals[ss].direction == "IN") {
|
||||
if (firstInit) this.notInitSignals[ this.circuit.signals[ss].component] --;
|
||||
callComponents.push(this.circuit.signals[ss].component);
|
||||
}
|
||||
}
|
||||
for (let i in callComponents) {
|
||||
const c= callComponents[i];
|
||||
if (this.notInitSignals[c] == 0) this.triggerComponent(c);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
setVar(name, sels, value) {
|
||||
function setVarArray(a, sels2, value) {
|
||||
if (sels2.length == 1) {
|
||||
a[sels2[0]] = value;
|
||||
} else {
|
||||
if (typeof(a[sels2[0]]) == "undefined") a[sels2[0]] = [];
|
||||
setVarArray(a[sels2[0]], sels2.slice(1), value);
|
||||
}
|
||||
}
|
||||
const scope = this.scopes[this.scopes.length-1];
|
||||
if (sels.length == 0) {
|
||||
scope[name] = value;
|
||||
} else {
|
||||
if (typeof(scope[name]) == "undefined") scope[name] = [];
|
||||
setVarArray(scope[name], sels, value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
getVar(name, sels) {
|
||||
function select(a, sels2) {
|
||||
return (sels2.length == 0) ? a : select(a[sels2[0]], sels2.slice(1));
|
||||
}
|
||||
for (let i=this.scopes.length-1; i>=0; i--) {
|
||||
if (typeof(this.scopes[i][name]) != "undefined") return select(this.scopes[i][name], sels);
|
||||
}
|
||||
throw new Error("Variable not defined: " + name);
|
||||
}
|
||||
|
||||
getSignal(name, sels) {
|
||||
let fullName = name=="one" ? "one" : this.currentComponent + "." + name;
|
||||
fullName += this._sels2str(sels);
|
||||
return this.getSignalFullName(fullName);
|
||||
}
|
||||
|
||||
|
||||
getPin(componentName, componentSels, signalName, signalSels) {
|
||||
let fullName = componentName=="one" ? "one" : this.currentComponent + "." + componentName;
|
||||
fullName += this._sels2str(componentSels) +
|
||||
"."+
|
||||
signalName+
|
||||
this._sels2str(signalSels);
|
||||
return this.getSignalFullName(fullName);
|
||||
}
|
||||
|
||||
getSignalFullName(fullName) {
|
||||
const sId = this.circuit.signals[fullName].id;
|
||||
if (typeof(this.witness[sId]) == "undefined") {
|
||||
throw new Error("Signal not initialized: "+fullName);
|
||||
}
|
||||
console.log("get --->" + fullName + " = " + this.witness[sId].toString() );
|
||||
return this.witness[sId];
|
||||
}
|
||||
|
||||
assert(a,b) {
|
||||
const ba = bigInt(a);
|
||||
const bb = bigInt(b);
|
||||
if (!ba.equals(bb)) {
|
||||
throw new Error("Constrain doesn't match: " + ba.toString() + " != " + bb.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
156
src/circuit_generator.js
Normal file
156
src/circuit_generator.js
Normal file
@@ -0,0 +1,156 @@
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const bigInt = require("big-integer");
|
||||
const __P__ = new bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
||||
const __MASK__ = new bigInt(2).pow(253).minus(1);
|
||||
const assert = require("assert");
|
||||
const gen = require("./gencode");
|
||||
const exec = require("./exec");
|
||||
const lc = require("./lcalgebra");
|
||||
|
||||
|
||||
const argv = require("optimist")
|
||||
.alias("c", "circuit")
|
||||
.alias("o", "output")
|
||||
.alias("w", "witnes")
|
||||
.argv;
|
||||
|
||||
const parser = require("../jaz.js").parser;
|
||||
|
||||
const fullFileName = path.resolve(process.cwd(), argv.circuit);
|
||||
const fullFilePath = path.dirname(fullFileName);
|
||||
|
||||
const src = fs.readFileSync(fullFileName, "utf8");
|
||||
const ast = parser.parse(src);
|
||||
|
||||
assert(ast.type == "BLOCK");
|
||||
|
||||
const ctx = {
|
||||
scopes: [{}],
|
||||
signals: {
|
||||
one: {
|
||||
fullName: "one",
|
||||
value: bigInt(1),
|
||||
equivalence: "",
|
||||
direction: ""
|
||||
}
|
||||
},
|
||||
currentComponent: "",
|
||||
constrains: [],
|
||||
components: {},
|
||||
templates: {},
|
||||
functions: {},
|
||||
functionParams: {},
|
||||
filePath: fullFilePath,
|
||||
fileName: fullFileName
|
||||
};
|
||||
|
||||
exec(ctx, ast);
|
||||
|
||||
reduceConstrains(ctx);
|
||||
generateWitnessNames(ctx);
|
||||
generateWitnessConstrains(ctx);
|
||||
|
||||
if (ctx.error) {
|
||||
console.log(`ERROR at ${ctx.error.errFile}:${ctx.error.pos.first_line},${ctx.error.pos.first_column}-${ctx.error.pos.last_line},${ctx.error.pos.last_column} ${ctx.error.errStr}`);
|
||||
console.log(JSON.stringify(ctx.error.ast, null, 1));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
console.log("SIGNALS");
|
||||
console.log("==========");
|
||||
for (let key in ctx.signals) {
|
||||
const signal = ctx.signals[key];
|
||||
console.log(signal.fullName);
|
||||
}
|
||||
|
||||
console.log("CONSTRAINS");
|
||||
console.log("==========");
|
||||
for (let i=0; i<ctx.constrains.length; i++) {
|
||||
console.log(lc.toString(ctx.constrains[i], ctx) + " === 0");
|
||||
}
|
||||
*/
|
||||
ctx.scopes = [{}];
|
||||
|
||||
const mainCode = gen(ctx,ast);
|
||||
if (ctx.error) {
|
||||
console.log(`ERROR at ${ctx.error.pos.first_line},${ctx.error.pos.first_column}-${ctx.error.pos.last_line},${ctx.error.pos.last_column} ${ctx.error.errStr}`);
|
||||
console.log(JSON.stringify(ctx.error.ast, null, 1));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
|
||||
let outCode = "";
|
||||
outCode += "const bigInt = require(\"big-integer\");\n";
|
||||
outCode += "const __P__ = new bigInt(\"21888242871839275222246405745257275088696311157297823662689037894645226208583\");\n";
|
||||
outCode += "const __MASK__ = new bigInt(2).pow(253).minus(1);\n";
|
||||
outCode += "const circuit = {};\n";
|
||||
outCode += "module.exports = circuit;\n\n";
|
||||
outCode += `circuit.signals=${JSON.stringify(ctx.signals, null, 1)};\n\n`;
|
||||
outCode += `circuit.components=${JSON.stringify(ctx.components, null, 1)};\n\n`;
|
||||
outCode += `circuit.signalConstrains=${JSON.stringify(ctx.constrains, null, 1)};\n\n`;
|
||||
outCode += `circuit.witnessNames=${JSON.stringify(ctx.witnessNames, null, 1)};\n\n`;
|
||||
outCode += mainCode;
|
||||
outCode += "\ncircuit.templates = {};\n";
|
||||
for (let t in ctx.templates) {
|
||||
outCode += `\ncircuit.templates["${t}"] = ${ctx.templates[t]};\n`;
|
||||
}
|
||||
outCode += `circuit.functionParams=${JSON.stringify(ctx.functionParams, null, 1)};\n\n`;
|
||||
outCode += "\ncircuit.functions = {};\n";
|
||||
for (let f in ctx.functions) {
|
||||
outCode += `\ncircuit.functions["${f}"] = ${ctx.functions[f]};\n`;
|
||||
}
|
||||
|
||||
/*
|
||||
console.log("CODE");
|
||||
console.log("====");
|
||||
console.log(outCode);
|
||||
*/
|
||||
|
||||
|
||||
console.log("#Constrains:" +ctx.constrains.length);
|
||||
|
||||
fs.writeFileSync(argv.output, outCode, "utf8");
|
||||
|
||||
function generateWitnessNames(ctx) {
|
||||
ctx.witnessNames = [];
|
||||
for (let c in ctx.components) {
|
||||
ctx.components[c].inputSignals = 0;
|
||||
}
|
||||
for (let s in ctx.signals) {
|
||||
const signal = ctx.signals[s];
|
||||
let lSignal = signal;
|
||||
while (lSignal.equivalence) {
|
||||
lSignal = ctx.signals[lSignal.equivalence];
|
||||
}
|
||||
if ( typeof(lSignal.id) === "undefined" ) {
|
||||
lSignal.id = ctx.witnessNames.length;
|
||||
ctx.witnessNames.push([]);
|
||||
}
|
||||
if (signal.direction == "IN") {
|
||||
ctx.components[signal.component].inputSignals++;
|
||||
}
|
||||
|
||||
signal.id = lSignal.id;
|
||||
ctx.witnessNames[signal.id].push(signal.fullName);
|
||||
}
|
||||
}
|
||||
|
||||
function reduceConstrains(ctx) {
|
||||
const newConstrains = [];
|
||||
for (let i=0; i<ctx.constrains.length; i++) {
|
||||
const c = lc.canonize(ctx, ctx.constrains[i]);
|
||||
if (!lc.isZero(c)) {
|
||||
newConstrains.push(c);
|
||||
}
|
||||
}
|
||||
ctx.constrains = newConstrains;
|
||||
}
|
||||
|
||||
function generateWitnessConstrains(ctx) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
874
src/exec.js
Normal file
874
src/exec.js
Normal file
@@ -0,0 +1,874 @@
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
|
||||
const bigInt = require("big-integer");
|
||||
const __P__ = new bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
||||
const __MASK__ = new bigInt(2).pow(253).minus(1);
|
||||
|
||||
const lc = require("./lcalgebra");
|
||||
const parser = require("../jaz.js").parser;
|
||||
|
||||
/* TODO: Add lines information
|
||||
|
||||
function setLines(dst, first, last) {
|
||||
last = last || first;
|
||||
dst.first_line = first.first_line;
|
||||
dst.first_column = first.first_column;
|
||||
dst.last_line = last.last_line;
|
||||
dst.last_column = last.last_column;
|
||||
}
|
||||
*/
|
||||
|
||||
module.exports = exec;
|
||||
|
||||
|
||||
function exec(ctx, ast) {
|
||||
if (!ast) {
|
||||
return error(ctx, ast, "Null AST");
|
||||
}
|
||||
if ((ast.type == "NUMBER") || (ast.type == "LINEARCOMBINATION") || (ast.type =="SIGNAL") || (ast.type == "QEQ")) {
|
||||
return ast;
|
||||
} else if (ast.type == "VARIABLE") {
|
||||
return execVariable(ctx, ast);
|
||||
} else if (ast.type == "PIN") {
|
||||
return execPin(ctx, ast);
|
||||
} else if (ast.type == "OP") {
|
||||
if (ast.op == "=") {
|
||||
return execVarAssignement(ctx, ast);
|
||||
} else if (ast.op == "<--") {
|
||||
return execSignalAssign(ctx, ast);
|
||||
} else if (ast.op == "<==") {
|
||||
return execSignalAssignConstrain(ctx, ast);
|
||||
} else if (ast.op == "===") {
|
||||
return execConstrain(ctx, ast);
|
||||
} else if (ast.op == "+=") {
|
||||
return execVarAddAssignement(ctx, ast);
|
||||
} else if (ast.op == "*=") {
|
||||
return execVarMulAssignement(ctx, ast);
|
||||
} else if (ast.op == "+") {
|
||||
return execAdd(ctx, ast);
|
||||
} else if (ast.op == "-") {
|
||||
return execSub(ctx, ast);
|
||||
} else if (ast.op == "UMINUS") {
|
||||
return execUMinus(ctx, ast);
|
||||
} else if (ast.op == "*") {
|
||||
return execMul(ctx, ast);
|
||||
} else if (ast.op == "PLUSPLUSRIGHT") {
|
||||
return execPlusPlusRight(ctx, ast);
|
||||
} else if (ast.op == "PLUSPLUSLEFT") {
|
||||
return execPlusPlusLeft(ctx, ast);
|
||||
} else if (ast.op == "**") {
|
||||
return execExp(ctx, ast);
|
||||
} else if (ast.op == "&") {
|
||||
return execBAnd(ctx, ast);
|
||||
} else if (ast.op == "<<") {
|
||||
return execShl(ctx, ast);
|
||||
} else if (ast.op == ">>") {
|
||||
return execShr(ctx, ast);
|
||||
} else if (ast.op == "<") {
|
||||
return execLt(ctx, ast);
|
||||
} else if (ast.op == "==") {
|
||||
return execEq(ctx, ast);
|
||||
} else if (ast.op == "?") {
|
||||
return execTerCon(ctx, ast);
|
||||
} else {
|
||||
error(ctx, ast, "Invalid operation: " + ast.op);
|
||||
}
|
||||
} else if (ast.type == "DECLARE") {
|
||||
if (ast.declareType == "COMPONENT") {
|
||||
return execDeclareComponent(ctx, ast);
|
||||
} else if ((ast.declareType == "SIGNALIN")||
|
||||
(ast.declareType == "SIGNALOUT")||
|
||||
(ast.declareType == "SIGNAL")) {
|
||||
return execDeclareSignal(ctx, ast);
|
||||
} else if (ast.declareType == "VARIABLE") {
|
||||
return execDeclareVariable(ctx, ast);
|
||||
} else {
|
||||
error(ctx, ast, "Invalid declaration: " + ast.declareType);
|
||||
}
|
||||
} else if (ast.type == "FUNCTIONCALL") {
|
||||
return execFunctionCall(ctx, ast);
|
||||
} else if (ast.type == "BLOCK") {
|
||||
return execBlock(ctx, ast);
|
||||
} else if (ast.type == "FOR") {
|
||||
return execFor(ctx, ast);
|
||||
} else if (ast.type == "WHILE") {
|
||||
return execWhile(ctx, ast);
|
||||
} else if (ast.type == "RETURN") {
|
||||
return execReturn(ctx, ast);
|
||||
} else if (ast.type == "TEMPLATEDEF") {
|
||||
return execTemplateDef(ctx, ast);
|
||||
} else if (ast.type == "FUNCTIONDEF") {
|
||||
return execFunctionDef(ctx, ast);
|
||||
} else if (ast.type == "INCLUDE") {
|
||||
return execInclude(ctx, ast);
|
||||
} else if (ast.type == "ARRAY") {
|
||||
return execArray(ctx, ast);
|
||||
} else {
|
||||
error(ctx, ast, "Invalid AST node type: " + ast.type);
|
||||
}
|
||||
}
|
||||
|
||||
function error(ctx, ast, errStr) {
|
||||
ctx.error = {
|
||||
pos: {
|
||||
first_line: ast.first_line,
|
||||
first_column: ast.first_column,
|
||||
last_line: ast.last_line,
|
||||
last_column: ast.last_column
|
||||
},
|
||||
errStr: errStr,
|
||||
errFile: ctx.fileName,
|
||||
ast: ast
|
||||
};
|
||||
}
|
||||
|
||||
function iterateSelectors(ctx, sizes, baseName, fn) {
|
||||
if (sizes.length == 0) {
|
||||
return fn(baseName);
|
||||
}
|
||||
const res = [];
|
||||
for (let i=0; i<sizes[0]; i++) {
|
||||
res.push(iterateSelectors(ctx, sizes.slice(1), baseName+"["+i+"]", fn));
|
||||
if (ctx.error) return null;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function setScope(ctx, name, selectors, value) {
|
||||
let l = getScopeLevel(ctx, name);
|
||||
if (l==-1) l= ctx.scopes.length-1;
|
||||
|
||||
if (selectors.length == 0) {
|
||||
ctx.scopes[l][name] = value;
|
||||
} else {
|
||||
setScopeArray(ctx.scopes[l][name], selectors);
|
||||
}
|
||||
|
||||
function setScopeArray(a, sels) {
|
||||
if (sels.length == 1) {
|
||||
a[sels[0]] = value;
|
||||
} else {
|
||||
setScopeArray(a[sels[0]], sels.slice(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getScope(ctx, name, selectors) {
|
||||
|
||||
const sels = [];
|
||||
if (selectors) {
|
||||
for (let i=0; i< selectors.length; i++) {
|
||||
const idx = exec(ctx, selectors[i]);
|
||||
if (ctx.error) return;
|
||||
|
||||
if (idx.type != "NUMBER") return error(ctx, selectors[i], "expected a number");
|
||||
sels.push( idx.value.toJSNumber() );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function select(v, s) {
|
||||
s = s || [];
|
||||
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 getScopeLevel(ctx, name) {
|
||||
for (let i=ctx.scopes.length-1; i>=0; i--) {
|
||||
if (ctx.scopes[i][name]) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
function execBlock(ctx, ast) {
|
||||
for (let i=0; i<ast.statements.length; i++) {
|
||||
exec(ctx, ast.statements[i]);
|
||||
if (ctx.returnValue) return;
|
||||
if (ctx.error) return;
|
||||
}
|
||||
}
|
||||
|
||||
function execTemplateDef(ctx, ast) {
|
||||
const scope = ctx.scopes[0]; // Lets put templates always in top scope.
|
||||
// const scope = ctx.scopes[ctx.scopes.length-1];
|
||||
if (getScope(ctx, ast.name)) {
|
||||
return error(ctx, ast, "Name already exists: "+ast.name);
|
||||
}
|
||||
scope[ast.name] = {
|
||||
type: "TEMPLATE",
|
||||
params: ast.params,
|
||||
block: ast.block,
|
||||
fileName: ctx.fileName,
|
||||
filePath: ctx.filePath,
|
||||
scopes: copyScope(ctx.scopes)
|
||||
};
|
||||
}
|
||||
|
||||
function execFunctionDef(ctx, ast) {
|
||||
const scope = ctx.scopes[0]; // Lets put functions always in top scope.
|
||||
// const scope = ctx.scopes[ctx.scopes.length-1];
|
||||
if (getScope(ctx, ast.name)) {
|
||||
return error(ctx, ast, "Name already exists: "+ast.name);
|
||||
}
|
||||
ctx.functionParams[ast.name] = ast.params;
|
||||
scope[ast.name] = {
|
||||
type: "FUNCTION",
|
||||
params: ast.params,
|
||||
block: ast.block,
|
||||
fileName: ctx.fileName,
|
||||
filePath: ctx.filePath,
|
||||
scopes: copyScope(ctx.scopes)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function execDeclareComponent(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 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]);
|
||||
if (ctx.error) return;
|
||||
|
||||
if (size.type != "NUMBER") return error(ctx, ast.name.selectors[i], "expected a number");
|
||||
|
||||
sizes.push( size.value.toJSNumber() );
|
||||
}
|
||||
|
||||
|
||||
scope[ast.name.name] = iterateSelectors(ctx, sizes, baseName, function(fullName) {
|
||||
|
||||
ctx.components[fullName] = "UNINSTANTIATED";
|
||||
|
||||
return {
|
||||
type: "COMPONENT",
|
||||
fullName: fullName
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
type: "VARIABLE",
|
||||
name: ast.name.name,
|
||||
selectors: []
|
||||
};
|
||||
}
|
||||
|
||||
function execInstantiateComponet(ctx, vr, fn) {
|
||||
|
||||
if (vr.type != "VARIABLE") return error(ctx, fn, "Left hand instatiate component must be a variable");
|
||||
if (fn.type != "FUNCTIONCALL") return error(ctx, fn, "Right type of instantiate component must be a function call");
|
||||
|
||||
const componentName = vr.name;
|
||||
const templateName = fn.name;
|
||||
|
||||
const scopeLevel = getScopeLevel(ctx, templateName);
|
||||
if (scopeLevel == -1) return error(ctx,fn, "Invalid Template");
|
||||
const template = getScope(ctx, templateName);
|
||||
|
||||
if (template.type != "TEMPLATE") return error(ctx, fn, "Invalid Template");
|
||||
|
||||
|
||||
const paramValues = [];
|
||||
for (let i=0; i< fn.params.length; i++) {
|
||||
const v = exec(ctx, fn.params[i]);
|
||||
if (ctx.error) return;
|
||||
|
||||
if (v.type != "NUMBER") return error(ctx, fn.params[i], "expected a number");
|
||||
paramValues.push( v.value);
|
||||
}
|
||||
if (template.params.length != paramValues.length) error(ctx, fn, "Invalid Number of parameters");
|
||||
|
||||
const vv = getScope(ctx, componentName, vr.selectors);
|
||||
|
||||
instantiateComponent(vv);
|
||||
|
||||
function instantiateComponent(varVal) {
|
||||
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");
|
||||
|
||||
const oldComponent = ctx.currentComponent;
|
||||
const oldFileName = ctx.fileName;
|
||||
const oldFilePath = ctx.filePath;
|
||||
ctx.currentComponent = varVal.fullName;
|
||||
|
||||
ctx.components[ctx.currentComponent] = {
|
||||
signals: [],
|
||||
params: {}
|
||||
};
|
||||
|
||||
const oldScopes = ctx.scopes;
|
||||
|
||||
ctx.scopes = oldScopes.slice(0, scopeLevel+1);
|
||||
|
||||
const scope = {};
|
||||
for (let i=0; i< template.params.length; i++) {
|
||||
scope[template.params[i]] = {
|
||||
type: "NUMBER",
|
||||
value: paramValues[i]
|
||||
};
|
||||
ctx.components[ctx.currentComponent].params[template.params[i]] = paramValues[i];
|
||||
}
|
||||
|
||||
ctx.components[ctx.currentComponent].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.scopes = oldScopes;
|
||||
}
|
||||
}
|
||||
|
||||
function execFunctionCall(ctx, ast) {
|
||||
|
||||
const scopeLevel = getScopeLevel(ctx, ast.name);
|
||||
if (scopeLevel == -1) return error(ctx, ast, "Function not defined: " + ast.name);
|
||||
const fnc = getScope(ctx, ast.name);
|
||||
|
||||
if (fnc.type != "FUNCTION") return error(ctx, ast, "Not a function: " + ast.name);
|
||||
|
||||
const paramValues = [];
|
||||
for (let i=0; i< ast.params.length; i++) {
|
||||
const v = exec(ctx, ast.params[i]);
|
||||
if (ctx.error) return;
|
||||
|
||||
if (v.type != "NUMBER") return error(ctx, ast.params[i], "expected a number");
|
||||
paramValues.push( v.value);
|
||||
}
|
||||
|
||||
if (ast.params.length != paramValues.length) error(ctx, ast, "Invalid Number of parameters");
|
||||
|
||||
const oldFileName = ctx.fileName;
|
||||
const oldFilePath = ctx.filePath;
|
||||
|
||||
const oldScopes = ctx.scopes;
|
||||
|
||||
ctx.scopes = oldScopes.slice(0, scopeLevel+1);
|
||||
|
||||
const scope = {};
|
||||
for (let i=0; i< fnc.params.length; i++) {
|
||||
scope[fnc.params[i]] = {
|
||||
type: "NUMBER",
|
||||
value: paramValues[i]
|
||||
};
|
||||
}
|
||||
|
||||
ctx.fileName = fnc.fileName;
|
||||
ctx.filePath = fnc.filePath;
|
||||
ctx.scopes = copyScope( fnc.scopes );
|
||||
ctx.scopes.push(scope);
|
||||
|
||||
execBlock(ctx, fnc.block);
|
||||
|
||||
const res = ctx.returnValue;
|
||||
ctx.returnValue = null;
|
||||
|
||||
ctx.fileName = oldFileName;
|
||||
ctx.filePath = oldFilePath;
|
||||
ctx.scopes = oldScopes;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
function execReturn(ctx, ast) {
|
||||
ctx.returnValue = exec(ctx, ast.value);
|
||||
return;
|
||||
}
|
||||
|
||||
function execDeclareSignal(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 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]);
|
||||
if (ctx.error) return;
|
||||
|
||||
if (size.type != "NUMBER") return error(ctx, ast.name.selectors[i], "expected a number");
|
||||
sizes.push( size.value.toJSNumber() );
|
||||
}
|
||||
|
||||
scope[ast.name.name] = iterateSelectors(ctx, sizes, baseName, function(fullName) {
|
||||
ctx.signals[fullName] = {
|
||||
fullName: fullName,
|
||||
direction: ast.declareType == "SIGNALIN" ? "IN" : (ast.declareType == "SIGNALOUT" ? "OUT" : ""),
|
||||
component: ctx.currentComponent,
|
||||
equivalence: "",
|
||||
alias: [fullName]
|
||||
};
|
||||
ctx.components[ctx.currentComponent].signals.push(fullName);
|
||||
return {
|
||||
type: "SIGNAL",
|
||||
fullName: fullName,
|
||||
};
|
||||
});
|
||||
return {
|
||||
type: "VARIABLE",
|
||||
name: ast.name.name,
|
||||
selectors: []
|
||||
};
|
||||
}
|
||||
|
||||
function execDeclareVariable(ctx, ast) {
|
||||
const scope = ctx.scopes[ctx.scopes.length-1];
|
||||
|
||||
if (ast.name.type != "VARIABLE") return error(ctx, ast, "Invalid linear combination name");
|
||||
if (getScope(ctx, ast.name.name)) return error(ctx, ast, "Name already exists: "+ast.name.name);
|
||||
|
||||
const sizes=[];
|
||||
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() );
|
||||
}
|
||||
|
||||
scope[ast.name.name] = iterateSelectors(ctx, sizes, "", function() {
|
||||
return {
|
||||
type: "NUMBER",
|
||||
value: bigInt(0)
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
type: "VARIABLE",
|
||||
name: ast.name.name,
|
||||
selectors: []
|
||||
};
|
||||
}
|
||||
|
||||
function execVariable(ctx, ast) {
|
||||
const v = getScope(ctx, ast.name, ast.selectors);
|
||||
if (ctx.error) return;
|
||||
|
||||
if (!v) return error(ctx, ast, "Variable not defined");
|
||||
let res;
|
||||
res=v;
|
||||
return res;
|
||||
}
|
||||
|
||||
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;
|
||||
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() + "]";
|
||||
}
|
||||
if (!ctx.signals[signalFullName]) error(ctx, ast, "Signal not defined:" + signalFullName);
|
||||
return {
|
||||
type: "SIGNAL",
|
||||
fullName: signalFullName
|
||||
};
|
||||
}
|
||||
|
||||
function execFor(ctx, ast) {
|
||||
exec(ctx, ast.init);
|
||||
if (ctx.error) return;
|
||||
|
||||
let v = exec(ctx, ast.condition);
|
||||
if (ctx.error) return;
|
||||
|
||||
while ((v.value.neq(0))&&(!ctx.returnValue)) {
|
||||
exec(ctx, ast.body);
|
||||
if (ctx.error) return;
|
||||
|
||||
exec(ctx, ast.step);
|
||||
if (ctx.error) return;
|
||||
|
||||
v = exec(ctx, ast.condition);
|
||||
if (ctx.error) return;
|
||||
}
|
||||
}
|
||||
|
||||
function execWhile(ctx, ast) {
|
||||
let v = exec(ctx, ast.condition);
|
||||
if (ctx.error) return;
|
||||
|
||||
while ((v.value.neq(0))&&(!ctx.returnValue)) {
|
||||
exec(ctx, ast.body);
|
||||
if (ctx.error) return;
|
||||
|
||||
v = exec(ctx, ast.condition);
|
||||
if (ctx.error) return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function execVarAssignement(ctx, ast) {
|
||||
let v;
|
||||
if (ast.values[0].type == "DECLARE") {
|
||||
v = exec(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
} else {
|
||||
v = ast.values[0];
|
||||
}
|
||||
const num = getScope(ctx, v.name, v.selectors);
|
||||
if (ctx.error) return;
|
||||
|
||||
if (typeof(num) != "object") return error(ctx, ast, "Variable not defined");
|
||||
|
||||
if (num.type == "COMPONENT") return execInstantiateComponet(ctx, v, ast.values[1]);
|
||||
|
||||
const res = exec(ctx, ast.values[1]);
|
||||
if (ctx.error) return;
|
||||
|
||||
setScope(ctx, v.name, v.selectors, res);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
function execLt(ctx, ast) {
|
||||
const a = exec(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
if (a.type != "NUMBER") return { type: "NUMBER" };
|
||||
const b = exec(ctx, ast.values[1]);
|
||||
if (ctx.error) return;
|
||||
if (b.type != "NUMBER") return { type: "NUMBER" };
|
||||
if (!a.value || !b.value) return { type: "NUMBER" };
|
||||
return {
|
||||
type: "NUMBER",
|
||||
value: a.value.lt(b.value) ? bigInt(1) : bigInt(0)
|
||||
};
|
||||
}
|
||||
|
||||
function execEq(ctx, ast) {
|
||||
const a = exec(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
if (a.type != "NUMBER") return { type: "NUMBER" };
|
||||
const b = exec(ctx, ast.values[1]);
|
||||
if (ctx.error) return;
|
||||
if (b.type != "NUMBER") return { type: "NUMBER" };
|
||||
if (!a.value || !b.value) return { type: "NUMBER" };
|
||||
return {
|
||||
type: "NUMBER",
|
||||
value: a.value.eq(b.value) ? bigInt(1) : bigInt(0)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function execBAnd(ctx, ast) {
|
||||
const a = exec(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
if (a.type != "NUMBER") return { type: "NUMBER" };
|
||||
const b = exec(ctx, ast.values[1]);
|
||||
if (ctx.error) return;
|
||||
if (b.type != "NUMBER") return { type: "NUMBER" };
|
||||
if (!a.value || !b.value) return { type: "NUMBER" };
|
||||
return {
|
||||
type: "NUMBER",
|
||||
value: a.value.and(b.value).and(__MASK__)
|
||||
};
|
||||
}
|
||||
|
||||
function execShl(ctx, ast) {
|
||||
const a = exec(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
if (a.type != "NUMBER") return { type: "NUMBER" };
|
||||
const b = exec(ctx, ast.values[1]);
|
||||
if (ctx.error) return;
|
||||
if (b.type != "NUMBER") return { type: "NUMBER" };
|
||||
if (!a.value || !b.value) return { type: "NUMBER" };
|
||||
const v = b.value.greater(256) ? 256 : b.value.value;
|
||||
return {
|
||||
type: "NUMBER",
|
||||
value: a.value.shiftLeft(v).and(__MASK__)
|
||||
};
|
||||
}
|
||||
|
||||
function execShr(ctx, ast) {
|
||||
const a = exec(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
if (a.type != "NUMBER") return { type: "NUMBER" };
|
||||
const b = exec(ctx, ast.values[1]);
|
||||
if (ctx.error) return;
|
||||
if (b.type != "NUMBER") return { type: "NUMBER" };
|
||||
if (!a.value || !b.value) return { type: "NUMBER" };
|
||||
const v = b.value.greater(256) ? 256 : b.value.value;
|
||||
return {
|
||||
type: "NUMBER",
|
||||
value: a.value.shiftRight(v).and(__MASK__)
|
||||
};
|
||||
}
|
||||
|
||||
function execExp(ctx, ast) {
|
||||
const a = exec(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
if (a.type != "NUMBER") return { type: "NUMBER" };
|
||||
const b = exec(ctx, ast.values[1]);
|
||||
if (ctx.error) return;
|
||||
if (b.type != "NUMBER") return { type: "NUMBER" };
|
||||
if (!a.value || !b.value) return { type: "NUMBER" };
|
||||
return {
|
||||
type: "NUMBER",
|
||||
value: a.value.modPow(b.value, __P__)
|
||||
};
|
||||
}
|
||||
|
||||
function execAdd(ctx, ast) {
|
||||
const a = exec(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
const b = exec(ctx, ast.values[1]);
|
||||
if (ctx.error) return;
|
||||
|
||||
const res = lc.add(a,b);
|
||||
if (res.type == "ERROR") return error(ctx, ast, res.errStr);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
function execSub(ctx, ast) {
|
||||
const a = exec(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
const b = exec(ctx, ast.values[1]);
|
||||
if (ctx.error) return;
|
||||
|
||||
const res = lc.sub(a,b);
|
||||
if (res.type == "ERROR") return error(ctx, ast, res.errStr);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
function execUMinus(ctx, ast) {
|
||||
const a = exec(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
|
||||
const res = lc.negate(a);
|
||||
if (res.type == "ERROR") return error(ctx, ast, res.errStr);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
function execMul(ctx, ast) {
|
||||
const a = exec(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
const b = exec(ctx, ast.values[1]);
|
||||
if (ctx.error) return;
|
||||
|
||||
const res = lc.mul(a,b);
|
||||
if (res.type == "ERROR") return error(ctx, ast, res.errStr);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
function execVarAddAssignement(ctx, ast) {
|
||||
const res = execAdd(ctx,{ values: [ast.values[0], ast.values[1]] } );
|
||||
if (ctx.error) return;
|
||||
return execVarAssignement(ctx, { values: [ast.values[0], res] });
|
||||
}
|
||||
|
||||
function execVarMulAssignement(ctx, ast) {
|
||||
const res = execMul(ctx,{ values: [ast.values[0], ast.values[1]] } );
|
||||
if (ctx.error) return;
|
||||
return execVarAssignement(ctx, { values: [ast.values[0], res] });
|
||||
}
|
||||
|
||||
function execPlusPlusRight(ctx, ast) {
|
||||
const resBefore = exec(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
const resAfter = execAdd(ctx,{ values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}] } );
|
||||
if (ctx.error) return;
|
||||
execVarAssignement(ctx, { values: [ast.values[0], resAfter] });
|
||||
return resBefore;
|
||||
}
|
||||
|
||||
function execPlusPlusLeft(ctx, ast) {
|
||||
if (ctx.error) return;
|
||||
const resAfter = execAdd(ctx,{ values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}] } );
|
||||
if (ctx.error) return;
|
||||
execVarAssignement(ctx, { values: [ast.values[0], resAfter] });
|
||||
return resAfter;
|
||||
}
|
||||
|
||||
function execTerCon(ctx, ast) {
|
||||
const cond = exec(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
|
||||
if (!cond.value) return { type: "NUMBER" };
|
||||
|
||||
if (cond.value.neq(0)) {
|
||||
return exec(ctx, ast.values[1]);
|
||||
} else {
|
||||
return exec(ctx, ast.values[2]);
|
||||
}
|
||||
}
|
||||
|
||||
function execSignalAssign(ctx, ast) {
|
||||
let vDest;
|
||||
if (ast.values[0].type == "DECLARE") {
|
||||
vDest = exec(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
} else {
|
||||
vDest = ast.values[0];
|
||||
}
|
||||
|
||||
let dst;
|
||||
if (vDest.type == "VARIABLE") {
|
||||
dst = getScope(ctx, vDest.name, vDest.selectors);
|
||||
if (ctx.error) return;
|
||||
} else if (vDest.type == "PIN") {
|
||||
dst = execPin(ctx, vDest);
|
||||
if (ctx.error) return;
|
||||
} else {
|
||||
error(ctx, ast, "Bad assignement");
|
||||
}
|
||||
|
||||
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);
|
||||
while (sDest.equivalence) sDest=ctx.signals[sDest.equivalence];
|
||||
|
||||
if (sDest.value) return error(ctx, ast, "Signals cannot be assigned twice");
|
||||
|
||||
let src = exec(ctx, ast.values[1]);
|
||||
if (ctx.error) return;
|
||||
|
||||
|
||||
/*
|
||||
let vSrc;
|
||||
if (ast.values[1].type == "DECLARE") {
|
||||
vSrc = exec(ctx, ast.values[1]);
|
||||
if (ctx.error) return;
|
||||
} else {
|
||||
vSrc = ast.values[1];
|
||||
}
|
||||
|
||||
if (vSrc.type == "VARIABLE") {
|
||||
src = getScope(ctx, vSrc.name, vSrc.selectors);
|
||||
if (!src) error(ctx, ast, "Variable not defined: " + vSrc.name);
|
||||
if (ctx.error) return;
|
||||
} else if (vSrc.type == "PIN") {
|
||||
src = execPin(ctx, vSrc);
|
||||
}
|
||||
*/
|
||||
|
||||
let assignValue = true;
|
||||
if (src.type == "SIGNAL") {
|
||||
sDest.equivalence = src.fullName;
|
||||
sDest.alias = sDest.alias.concat(src.alias);
|
||||
while (sDest.equivalence) sDest=ctx.signals[sDest.equivalence];
|
||||
assignValue = false;
|
||||
}
|
||||
|
||||
if (assignValue) {
|
||||
// const resLC = exec(ctx, vSrc);
|
||||
if (ctx.error) return;
|
||||
|
||||
// const v = lc.evaluate(ctx, resLC);
|
||||
const v = lc.evaluate(ctx, src);
|
||||
|
||||
if (v.value) {
|
||||
sDest.value = v.value;
|
||||
}
|
||||
}
|
||||
|
||||
return vDest;
|
||||
}
|
||||
|
||||
function execConstrain(ctx, ast) {
|
||||
const a = exec(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
const b = exec(ctx, ast.values[1]);
|
||||
if (ctx.error) return;
|
||||
|
||||
const res = lc.sub(a,b);
|
||||
if (res.type == "ERROR") return error(ctx, ast, res.errStr);
|
||||
|
||||
if (!lc.isZero(res)) {
|
||||
ctx.constrains.push(lc.toQEQ(res));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
function execSignalAssignConstrain(ctx, ast) {
|
||||
const v = execSignalAssign(ctx,ast);
|
||||
if (ctx.error) return;
|
||||
execConstrain(ctx, ast);
|
||||
if (ctx.error) return;
|
||||
return v;
|
||||
}
|
||||
|
||||
function execInclude(ctx, ast) {
|
||||
const incFileName = path.resolve(ctx.filePath, ast.file);
|
||||
const incFilePath = path.dirname(incFileName);
|
||||
|
||||
console.log("Include: "+incFileName);
|
||||
|
||||
ctx.includedFiles = ctx.includedFiles || [];
|
||||
if (ctx.includedFiles[incFileName]) return;
|
||||
|
||||
ctx.includedFiles[incFileName] = true;
|
||||
|
||||
const src = fs.readFileSync(incFileName, "utf8");
|
||||
|
||||
if (!src) return error(ctx, ast, "Include file not found: "+incFileName);
|
||||
|
||||
const incAst = parser.parse(src);
|
||||
|
||||
const oldFilePath = ctx.filePath;
|
||||
const oldFileName = ctx.fileName;
|
||||
ctx.filePath = incFilePath;
|
||||
ctx.fileName = incFileName;
|
||||
|
||||
exec(ctx, incAst);
|
||||
|
||||
ast.block = incAst;
|
||||
|
||||
ctx.filePath = oldFilePath;
|
||||
ctx.fileName = oldFileName;
|
||||
}
|
||||
|
||||
function execArray(ctx, ast) {
|
||||
const res = [];
|
||||
|
||||
for (let i=0; i<ast.values.length; i++) {
|
||||
res.push(exec(ctx, ast.values[i]));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
function copyScope(scope) {
|
||||
var scopesClone = [];
|
||||
for (let i=0; i<scope.length; i++) {
|
||||
scopesClone.push(scope[i]);
|
||||
}
|
||||
return scopesClone;
|
||||
}
|
||||
|
||||
479
src/gencode.js
Normal file
479
src/gencode.js
Normal file
@@ -0,0 +1,479 @@
|
||||
const bigInt = require("big-integer");
|
||||
|
||||
module.exports = gen;
|
||||
|
||||
|
||||
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 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 genConstrain(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 == "PLUSPLUSRIGHT") {
|
||||
return genPlusPlusRight(ctx, ast);
|
||||
} else if (ast.op == "PLUSPLUSLEFT") {
|
||||
return genPlusPlusLeft(ctx, ast);
|
||||
} else if (ast.op == "**") {
|
||||
return genExp(ctx, ast);
|
||||
} else if (ast.op == "&") {
|
||||
return genBAnd(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 genEq(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 == "FOR") {
|
||||
return genFor(ctx, ast);
|
||||
} else if (ast.type == "WHILE") {
|
||||
return genWhile(ctx, ast);
|
||||
} else if (ast.type == "RETURN") {
|
||||
return genReturn(ctx, ast);
|
||||
} else if (ast.type == "TEMPLATEDEF") {
|
||||
return genTemplateDef(ctx, ast);
|
||||
} else if (ast.type == "FUNCTIONDEF") {
|
||||
return genFunctionDef(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 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
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
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 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 genBlock(ctx, ast) {
|
||||
let body = "";
|
||||
for (let i=0; i<ast.statements.length; i++) {
|
||||
const l = gen(ctx, ast.statements[i]);
|
||||
if (ctx.error) return;
|
||||
if (l) {
|
||||
body += l;
|
||||
if (body[body.length-1] != "\n") body += ";\n";
|
||||
}
|
||||
}
|
||||
return "{\n"+ident(body)+"}\n";
|
||||
}
|
||||
|
||||
function genTemplateDef(ctx, ast) {
|
||||
let S = "function(ctx) ";
|
||||
|
||||
const newScope = {};
|
||||
for (let i=0; i< ast.params.length; i++) {
|
||||
newScope[ast.params[i]] = { type: "VARIABLE" };
|
||||
}
|
||||
|
||||
ctx.scopes.push(newScope);
|
||||
S += genBlock(ctx, ast.block);
|
||||
ctx.scopes.pop();
|
||||
|
||||
// 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 "";
|
||||
}
|
||||
|
||||
function genFunctionDef(ctx, ast) {
|
||||
let S = "function(ctx) ";
|
||||
|
||||
const newScope = {};
|
||||
const params = [];
|
||||
for (let i=0; i< ast.params.length; i++) {
|
||||
newScope[ast.params[i]] = { type: "VARIABLE" };
|
||||
params.push(ast.params[i]);
|
||||
}
|
||||
|
||||
ctx.scopes.push(newScope);
|
||||
S += genBlock(ctx, ast.block);
|
||||
ctx.scopes.pop();
|
||||
|
||||
// const scope = ctx.scopes[0]; // Scope for templates is top
|
||||
const scope = ctx.scopes[ctx.scopes.length-1];
|
||||
|
||||
scope[ast.name] = {
|
||||
type: "FUNCTION"
|
||||
};
|
||||
|
||||
ctx.functions[ast.name] = S;
|
||||
ctx.functionParams[ast.name] = params;
|
||||
return "";
|
||||
}
|
||||
|
||||
function genFor(ctx, ast) {
|
||||
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;
|
||||
return `for (${init};${condition};${step})\n${body}`;
|
||||
}
|
||||
|
||||
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 (${condition})\n${body}`;
|
||||
}
|
||||
|
||||
function genReturn(ctx, ast) {
|
||||
const value = gen(ctx, ast.value);
|
||||
if (ctx.error) return;
|
||||
return `return ${value};`;
|
||||
}
|
||||
|
||||
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"
|
||||
};
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
scope[ast.name.name] = {
|
||||
type: "SIGNAL"
|
||||
};
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
function genDeclareVariable(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: "VARIABLE"
|
||||
};
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
function genNumber(ctx, ast) {
|
||||
return `"${ast.value.toString()}"`;
|
||||
}
|
||||
|
||||
function genVariable(ctx, ast) {
|
||||
const v = getScope(ctx, ast.name);
|
||||
|
||||
const sels = [];
|
||||
for (let i=0; i<ast.selectors.length; i++) {
|
||||
sels.push(gen(ctx, ast.selectors[i]));
|
||||
if (ctx.error) return;
|
||||
}
|
||||
|
||||
|
||||
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 genPin(ctx, ast) {
|
||||
let componentName = ast.component.name;
|
||||
let componentSelectors = [];
|
||||
for (let i=0; i<ast.component.selectors.length; i++) {
|
||||
componentSelectors.push(gen(ctx, ast.component.selectors[i]));
|
||||
}
|
||||
componentSelectors = "["+ componentSelectors.join(",") + "]";
|
||||
let pinName = ast.pin.name;
|
||||
let pinSelectors = [];
|
||||
for (let i=0; i<ast.pin.selectors.length; i++) {
|
||||
pinSelectors.push(gen(ctx, ast.pin.selectors[i]));
|
||||
}
|
||||
pinSelectors = "["+ pinSelectors.join(",") + "]";
|
||||
return `ctx.getPin("${componentName}", ${componentSelectors}, "${pinName}", ${pinSelectors})`;
|
||||
}
|
||||
|
||||
function genVarAssignement(ctx, ast) {
|
||||
|
||||
const sels = [];
|
||||
let vName;
|
||||
|
||||
if (ctx.error) return;
|
||||
|
||||
if (ast.values[0].type == "PIN") {
|
||||
let componentName = ast.values[0].component.name;
|
||||
let componentSelectors = [];
|
||||
for (let i=0; i<ast.values[0].component.selectors.length; i++) {
|
||||
componentSelectors.push(gen(ctx, ast.values[0].component.selectors[i]));
|
||||
}
|
||||
componentSelectors = "["+ componentSelectors.join(",") + "]";
|
||||
let pinName = ast.values[0].pin.name;
|
||||
let pinSelectors = [];
|
||||
for (let i=0; i<ast.values[0].pin.selectors.length; i++) {
|
||||
pinSelectors.push(gen(ctx, ast.values[0].pin.selectors[i]));
|
||||
}
|
||||
pinSelectors = "["+ pinSelectors.join(",") + "]";
|
||||
const res = gen(ctx, ast.values[1]);
|
||||
return `ctx.setPin("${componentName}", ${componentSelectors}, "${pinName}", ${pinSelectors}, ${res})`;
|
||||
}
|
||||
|
||||
if (ast.values[0].type == "DECLARE") {
|
||||
gen(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
vName = ast.values[0].name.name;
|
||||
} else {
|
||||
vName = ast.values[0].name;
|
||||
for (let i=0; i<ast.values[0].selectors.length; i++) {
|
||||
sels.push(gen(ctx, ast.values[0].selectors[i]));
|
||||
if (ctx.error) return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const v = getScope(ctx, vName);
|
||||
|
||||
// Component instantiation is already done.
|
||||
if (v.type == "COMPONENT") return "";
|
||||
|
||||
const res = gen(ctx, ast.values[1]);
|
||||
if (v.type == "VARIABLE") {
|
||||
return `ctx.setVar("${vName}", [${sels.join(",")}], ${res})`;
|
||||
} else if (v.type == "SIGNAL") {
|
||||
return `ctx.setSignal("${vName}", [${sels.join(",")}], ${res})`;
|
||||
} else {
|
||||
return error(ctx, ast, "Assigning to invalid");
|
||||
}
|
||||
}
|
||||
|
||||
function genConstrain(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 `ctx.assert(${a}, ${b})`;
|
||||
}
|
||||
|
||||
function genSignalAssignConstrain(ctx, ast) {
|
||||
return genVarAssignement(ctx, ast) + ";\n" + genConstrain(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__).minus(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 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(${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}).times(${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__).minus(${b}).mod(__P__)`;
|
||||
}
|
||||
|
||||
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(${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(${b}).and(__MASK__)`;
|
||||
}
|
||||
|
||||
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(256) ? 0 : bigInt(${a}).shiftLeft(bigInt(${b}).value).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(256) ? 0 : bigInt(${a}).shiftRight(bigInt(${b}).value).and(__MASK__)`;
|
||||
}
|
||||
|
||||
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(${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(${b}) ? 1 : 0`;
|
||||
}
|
||||
|
||||
function genUMinus(ctx, ast) {
|
||||
const a = gen(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
return `__P__.minus(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(0) ? (${b}) : (${c})`;
|
||||
}
|
||||
|
||||
function genInclude(ctx, ast) {
|
||||
return ast.block ? gen(ctx, ast.block) : "";
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
441
src/lcalgebra.js
Normal file
441
src/lcalgebra.js
Normal file
@@ -0,0 +1,441 @@
|
||||
/*
|
||||
|
||||
NUMBER: a
|
||||
|
||||
{
|
||||
type: "NUMBER",
|
||||
value: bigInt(a)
|
||||
}
|
||||
|
||||
LINEARCOMBINATION: c1*s1 + c2*s2 + c3*s3
|
||||
|
||||
{
|
||||
type: "LINEARCOMBINATION",
|
||||
values: {
|
||||
s1: bigInt(c1),
|
||||
s2: bigInt(c2),
|
||||
s3: bigInt(c3)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QEQ: a*b + c WHERE a,b,c are LINEARCOMBINATION
|
||||
{
|
||||
type: "QEQ"
|
||||
a: { type: LINEARCOMBINATION, values: {...} },
|
||||
b: { type: LINEARCOMBINATION, values: {...} },
|
||||
c: { type: LINEARCOMBINATION, values: {...} }
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
+ NUM LC QEQ
|
||||
NUM NUM LC QEQ
|
||||
LC LC LC QEQ
|
||||
QEQ QEQ QEQ ERR
|
||||
|
||||
* NUM LC QEQ
|
||||
NUM NUM LC QEQ
|
||||
LC LC QEQ ERR
|
||||
QEQ QEQ ERR ERR
|
||||
*/
|
||||
|
||||
const bigInt = require("big-integer");
|
||||
const __P__ = new bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
||||
|
||||
exports.add = add;
|
||||
exports.mul = mul;
|
||||
exports.evaluate = evaluate;
|
||||
exports.negate = negate;
|
||||
exports.sub = sub;
|
||||
exports.toQEQ = toQEQ;
|
||||
exports.isZero = isZero;
|
||||
exports.toString = toString;
|
||||
exports.canonize = canonize;
|
||||
|
||||
function signal2lc(a) {
|
||||
let lc;
|
||||
if (a.type == "SIGNAL") {
|
||||
lc = {
|
||||
type: "LINEARCOMBINATION",
|
||||
values: {}
|
||||
};
|
||||
lc.values[a.fullName] = bigInt(1);
|
||||
return lc;
|
||||
} else {
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
function clone(a) {
|
||||
const res = {};
|
||||
res.type = a.type;
|
||||
if (a.type == "NUMBER") {
|
||||
res.value = bigInt(a.value);
|
||||
} else if (a.type == "LINEARCOMBINATION") {
|
||||
res.values = {};
|
||||
for (let k in a.values) {
|
||||
res.values[k] = bigInt(a.values[k]);
|
||||
}
|
||||
} else if (a.type == "QEQ") {
|
||||
res.a = clone(a.a);
|
||||
res.b = clone(a.b);
|
||||
res.c = clone(a.c);
|
||||
} else if (a.type == "ERROR") {
|
||||
res.errStr = a.errStr;
|
||||
} else {
|
||||
res.type = "ERROR";
|
||||
res.errStr = "Invilid type when clonning: "+a.type;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function add(_a, _b) {
|
||||
const a = signal2lc(_a);
|
||||
const b = signal2lc(_b);
|
||||
if (a.type == "ERROR") return a;
|
||||
if (b.type == "ERROR") return b;
|
||||
if (a.type == "NUMBER") {
|
||||
if (b.type == "NUMBER") {
|
||||
return addNumNum(a,b);
|
||||
} else if (b.type=="LINEARCOMBINATION") {
|
||||
return addLCNum(b,a);
|
||||
} else if (b.type=="QEQ") {
|
||||
return addQEQNum(b,a);
|
||||
} else {
|
||||
return { type: "ERROR", errStr: "LC Add Invalid Type 2: "+b.type };
|
||||
}
|
||||
} else if (a.type=="LINEARCOMBINATION") {
|
||||
if (b.type == "NUMBER") {
|
||||
return addLCNum(a,b);
|
||||
} else if (b.type=="LINEARCOMBINATION") {
|
||||
return addLCLC(a,b);
|
||||
} else if (b.type=="QEQ") {
|
||||
return addQEQLC(b,a);
|
||||
} else {
|
||||
return { type: "ERROR", errStr: "LC Add Invalid Type 2: "+b.type };
|
||||
}
|
||||
} else if (a.type=="QEQ") {
|
||||
if (b.type == "NUMBER") {
|
||||
return addQEQNum(a,b);
|
||||
} else if (b.type=="LINEARCOMBINATION") {
|
||||
return addQEQLC(a,b);
|
||||
} else if (b.type=="QEQ") {
|
||||
return { type: "ERROR", errStr: "QEQ + QEQ" };
|
||||
} else {
|
||||
return { type: "ERROR", errStr: "LC Add Invalid Type 2: "+b.type };
|
||||
}
|
||||
} else {
|
||||
return { type: "ERROR", errStr: "LC Add Invalid Type 1: "+a.type };
|
||||
}
|
||||
}
|
||||
|
||||
function addNumNum(a,b) {
|
||||
if (!a.value || !b.value) return { type: "NUMBER" };
|
||||
return {
|
||||
type: "NUMBER",
|
||||
value: a.value.add(b.value).mod(__P__)
|
||||
};
|
||||
}
|
||||
|
||||
function addLCNum(a,b) {
|
||||
let res = clone(a);
|
||||
if (!b.value) {
|
||||
return { type: "ERROR", errStr: "LinearCombination + undefined" };
|
||||
}
|
||||
if (b.value.isZero()) return res;
|
||||
if (!res.values["one"]) {
|
||||
res.values["one"]=bigInt(b.value);
|
||||
} else {
|
||||
res.values["one"]= res.values["one"].add(b.value).mod(__P__);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function addLCLC(a,b) {
|
||||
let res = clone(a);
|
||||
for (let k in b.values) {
|
||||
if (!res.values[k]) {
|
||||
res.values[k]=bigInt(b.values[k]);
|
||||
} else {
|
||||
res.values[k]= res.values[k].add(b.values[k]).mod(__P__);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function addQEQNum(a,b) {
|
||||
let res = clone(a);
|
||||
res.c = addLCNum(res.c, b);
|
||||
if (res.c.type == "ERROR") return res.c;
|
||||
return res;
|
||||
}
|
||||
|
||||
function addQEQLC(a,b) {
|
||||
let res = clone(a);
|
||||
res.c = addLCLC(res.c, b);
|
||||
if (res.c.type == "ERROR") return res.c;
|
||||
return res;
|
||||
}
|
||||
|
||||
function mul(_a, _b) {
|
||||
const a = signal2lc(_a);
|
||||
const b = signal2lc(_b);
|
||||
if (a.type == "ERROR") return a;
|
||||
if (b.type == "ERROR") return b;
|
||||
if (a.type == "NUMBER") {
|
||||
if (b.type == "NUMBER") {
|
||||
return mulNumNum(a,b);
|
||||
} else if (b.type=="LINEARCOMBINATION") {
|
||||
return mulLCNum(b,a);
|
||||
} else if (b.type=="QEQ") {
|
||||
return mulQEQNum(b,a);
|
||||
} else {
|
||||
return { type: "ERROR", errStr: "LC Mul Invalid Type 2: "+b.type };
|
||||
}
|
||||
} else if (a.type=="LINEARCOMBINATION") {
|
||||
if (b.type == "NUMBER") {
|
||||
return mulLCNum(a,b);
|
||||
} else if (b.type=="LINEARCOMBINATION") {
|
||||
return mulLCLC(a,b);
|
||||
} else if (b.type=="QEQ") {
|
||||
return { type: "ERROR", errStr: "LC * QEQ" };
|
||||
} else {
|
||||
return { type: "ERROR", errStr: "LC Mul Invalid Type 2: "+b.type };
|
||||
}
|
||||
} else if (a.type=="QEQ") {
|
||||
if (b.type == "NUMBER") {
|
||||
return mulQEQNum(a,b);
|
||||
} else if (b.type=="LINEARCOMBINATION") {
|
||||
return { type: "ERROR", errStr: "QEC * LC" };
|
||||
} else if (b.type=="QEQ") {
|
||||
return { type: "ERROR", errStr: "QEQ * QEQ" };
|
||||
} else {
|
||||
return { type: "ERROR", errStr: "LC Mul Invalid Type 2: "+b.type };
|
||||
}
|
||||
} else {
|
||||
return { type: "ERROR", errStr: "LC Mul Invalid Type 1: "+a.type };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function mulNumNum(a,b) {
|
||||
if (!a.value || !b.value) return { type: "NUMBER" };
|
||||
return {
|
||||
type: "NUMBER",
|
||||
value: a.value.times(b.value).mod(__P__)
|
||||
};
|
||||
}
|
||||
|
||||
function mulLCNum(a,b) {
|
||||
let res = clone(a);
|
||||
if (!b.value) {
|
||||
return {type: "ERROR", errStr: "LinearCombination * undefined"};
|
||||
}
|
||||
for (let k in res.values) {
|
||||
res.values[k] = res.values[k].times(b.value).mod(__P__);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function mulLCLC(a,b) {
|
||||
return {
|
||||
type: "QEQ",
|
||||
a: clone(a),
|
||||
b: clone(b),
|
||||
c: { type: "LINEARCOMBINATION", values: {}}
|
||||
};
|
||||
}
|
||||
|
||||
function mulQEQNum(a,b) {
|
||||
let res = {
|
||||
type: "QEQ",
|
||||
a: mulLCNum(a.a, b),
|
||||
b: clone(a.b),
|
||||
c: mulLCNum(a.c, b)
|
||||
};
|
||||
if (res.a.type == "ERROR") return res.a;
|
||||
if (res.c.type == "ERROR") return res.a;
|
||||
return res;
|
||||
}
|
||||
|
||||
function getSignalValue(ctx, signalName) {
|
||||
const s = ctx.signals[signalName];
|
||||
if (s.equivalence != "") {
|
||||
return getSignalValue(ctx, s.equivalence);
|
||||
} else {
|
||||
const res = {
|
||||
type: "NUMBER"
|
||||
};
|
||||
if (s.value) {
|
||||
res.value = s.value;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
function evaluate(ctx, n) {
|
||||
if (n.type == "NUMBER") {
|
||||
return n;
|
||||
} else if (n.type == "SIGNAL") {
|
||||
return getSignalValue(ctx, n.fullName);
|
||||
} else if (n.type == "LINEARCOMBINATION") {
|
||||
const v= {
|
||||
type: "NUMBER",
|
||||
value: bigInt(0)
|
||||
};
|
||||
for (let k in n.values) {
|
||||
const s = getSignalValue(ctx, k);
|
||||
if (s.type != "NUMBER") return {type: "ERROR", errStr: "Invalid signal in linear Combination: " + k};
|
||||
if (!s.value) return { type: "NUMBER" };
|
||||
v.value = v.value.add( n.values[k].times(s.value)).mod(__P__);
|
||||
}
|
||||
return v;
|
||||
} else if (n.type == "QEQ") {
|
||||
const a = evaluate(ctx, n.a);
|
||||
if (a.type == "ERROR") return a;
|
||||
if (!a.value) return { type: "NUMBER" };
|
||||
const b = evaluate(ctx, n.b);
|
||||
if (b.type == "ERROR") return b;
|
||||
if (!b.value) return { type: "NUMBER" };
|
||||
const c = evaluate(ctx, n.c);
|
||||
if (c.type == "ERROR") return c;
|
||||
if (!c.value) return { type: "NUMBER" };
|
||||
|
||||
return {
|
||||
type: "NUMBER",
|
||||
value: (a.value.times(b.value).add(c.value)).mod(__P__)
|
||||
};
|
||||
} else if (n.type == "ERROR") {
|
||||
return n;
|
||||
} else {
|
||||
return {type: "ERROR", errStr: "Invalid type in evaluate: "+n.type};
|
||||
}
|
||||
}
|
||||
|
||||
function negate(_a) {
|
||||
const a = signal2lc(_a);
|
||||
let res = clone(a);
|
||||
if (res.type == "NUMBER") {
|
||||
res.value = __P__.minus(a.value).mod(__P__);
|
||||
} else if (res.type == "LINEARCOMBINATION") {
|
||||
for (let k in res.values) {
|
||||
res.values[k] = __P__.minus(res.values[k]).mod(__P__);
|
||||
}
|
||||
} else if (res.type == "QEQ") {
|
||||
res.a = negate(res.a);
|
||||
res.c = negate(res.c);
|
||||
} else if (res.type == "ERROR") {
|
||||
return res;
|
||||
} else {
|
||||
res = {type: "ERROR", errStr: "LC Negate invalid Type: "+res.type};
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function sub(a, b) {
|
||||
return add(a, negate(b));
|
||||
}
|
||||
|
||||
function toQEQ(a) {
|
||||
if (a.type == "NUMBER") {
|
||||
return {
|
||||
type: "QEQ",
|
||||
a: {type: "LINEARCOMBINATION", values: {}},
|
||||
b: {type: "LINEARCOMBINATION", values: {}},
|
||||
c: {type: "LINEARCOMBINATION", values: {"one": bigInt(a.value)}}
|
||||
};
|
||||
} else if (a.type == "LINEARCOMBINATION") {
|
||||
return {
|
||||
type: "QEQ",
|
||||
a: {type: "LINEARCOMBINATION", values: {}},
|
||||
b: {type: "LINEARCOMBINATION", values: {}},
|
||||
c: clone(a)
|
||||
};
|
||||
} else if (a.type == "QEQ") {
|
||||
return clone(a);
|
||||
} else if (a.type == "ERROR") {
|
||||
return clone(a);
|
||||
} else {
|
||||
return {type: "ERROR", errStr: "toQEQ invalid Type: "+a.type};
|
||||
}
|
||||
}
|
||||
|
||||
function isZero(a) {
|
||||
if (a.type == "NUMBER") {
|
||||
return a.value.isZero();
|
||||
} else if (a.type == "LINEARCOMBINATION") {
|
||||
for (let k in a.values) {
|
||||
if (!a.values[k].isZero()) return false;
|
||||
}
|
||||
return true;
|
||||
} else if (a.type == "QEQ") {
|
||||
return (isZero(a.a) || isZero(a.b)) && isZero(a.c);
|
||||
} else if (a.type == "ERROR") {
|
||||
return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function toString(a, ctx) {
|
||||
if (a.type == "NUMBER") {
|
||||
return a.value.toString();
|
||||
} else if (a.type == "LINEARCOMBINATION") {
|
||||
let S="";
|
||||
for (let k in a.values) {
|
||||
if (!a.values[k].isZero()) {
|
||||
let c;
|
||||
if (a.values[k].greater(__P__.divide(2))) {
|
||||
S = S + "-";
|
||||
c = __P__.minus(a.values[k]);
|
||||
} else {
|
||||
if (S!="") S=S+" + ";
|
||||
c = a.values[k];
|
||||
}
|
||||
if (!c.equals(1)) {
|
||||
S = S + c.toString() + "*";
|
||||
}
|
||||
let sigName = k;
|
||||
if (ctx) {
|
||||
while (ctx.signals[sigName].equivalence) sigName = ctx.signals[sigName].equivalence;
|
||||
}
|
||||
S = S + sigName;
|
||||
}
|
||||
}
|
||||
if (S=="") return "0"; else return S;
|
||||
} else if (a.type == "QEQ") {
|
||||
return "( "+toString(a.a, ctx)+" ) * ( "+toString(a.b, ctx)+" ) + " + toString(a.c, ctx);
|
||||
} else if (a.type == "ERROR") {
|
||||
return "ERROR: "+a.errStr;
|
||||
} else {
|
||||
return "INVALID";
|
||||
}
|
||||
}
|
||||
|
||||
function canonize(ctx, a) {
|
||||
if (a.type == "LINEARCOMBINATION") {
|
||||
for (let k in a.values) {
|
||||
let s = k;
|
||||
while (ctx.signals[s].equivalence) s= ctx.signals[s].equivalence;
|
||||
if (s != k) {
|
||||
if (!a.values[s]) {
|
||||
a.values[s]=bigInt(a.values[k]);
|
||||
} else {
|
||||
a.values[s]= a.values[s].add(a.values[k]).mod(__P__);
|
||||
}
|
||||
delete a.values[k];
|
||||
}
|
||||
}
|
||||
for (let k in a.values) {
|
||||
if (a.values[k].isZero()) delete a.values[k];
|
||||
}
|
||||
return a;
|
||||
} else if (a.type == "QEQ") {
|
||||
a.a = canonize(ctx, a.a);
|
||||
a.b = canonize(ctx, a.b);
|
||||
a.c = canonize(ctx, a.c);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
27
src/rt.js
Normal file
27
src/rt.js
Normal file
@@ -0,0 +1,27 @@
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const calculateWitness = require("./calculateWitness.js");
|
||||
|
||||
const argv = require("optimist")
|
||||
.alias("i", "input")
|
||||
.alias("o", "output")
|
||||
.alias("c", "circuit")
|
||||
.argv;
|
||||
|
||||
const circuit = require(path.resolve(argv.circuit));
|
||||
|
||||
const inputSignals = JSON.parse(fs.readFileSync(argv.input, "utf8"));
|
||||
|
||||
try {
|
||||
const w = calculateWitness(circuit, inputSignals);
|
||||
fs.writeFileSync(argv.output, JSON.stringify(w), "utf8");
|
||||
} catch(err) {
|
||||
console.log("ERROR: " + err);
|
||||
console.log(err.stack);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user