arnaucube
arnaucube/circomPublic Self-hosted repository
circom/src/construction_phase.js
src/construction_phase.jsRaw
   1/*
   2    Copyright 2018 0KIMS association.
   3
   4    This file is part of circom (Zero Knowledge Circuit Compiler).
   5
   6    circom is a free software: you can redistribute it and/or modify it
   7    under the terms of the GNU General Public License as published by
   8    the Free Software Foundation, either version 3 of the License, or
   9    (at your option) any later version.
  10
  11    circom is distributed in the hope that it will be useful, but WITHOUT
  12    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  13    or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
  14    License for more details.
  15
  16    You should have received a copy of the GNU General Public License
  17    along with circom. If not, see <https://www.gnu.org/licenses/>.
  18*/
  19
  20/*
  21
  22    The exec functions, return an object of the form:
  23
  24    {
  25        t: "C" or "V" or "S"
  26        s: Accumulated Sizes
  27        v: [] only when "V" Array of values where each value is:
  28            {
  29                t: "N", "S", "LC", "QEX", "NQ"
  30                v: When "N" its the bigInt of the number
  31                coefs: { sIdx: vCoef } when "LC" the values of the coefs
  32                a,b,c: a LC object of a quadratic expression.
  33            }
  34        refId: In case of a variable, the reference of a variable.
  35        sIdx: When signal, the sIdx
  36        cIdx: When component, the cIdx
  37    }
  38
  39
  40
  41
  42
  43 */
  44
  45const path = require("path");
  46const fs = require("fs");
  47const assert = require("assert");
  48
  49const iterateAST = require("./iterateast");
  50const utils = require("./utils");
  51
  52const LCAlgebra = require("./lcalgebra");
  53const parser = require("../parser/jaz.js").parser;
  54const Scalar = require("ffjavascript").Scalar;
  55
  56const {stringifyBigInts} = require("ffjavascript").utils;
  57
  58/* TODO: Add lines information
  59
  60function setLines(dst, first, last) {
  61    last = last || first;
  62    dst.first_line = first.first_line;
  63    dst.first_column = first.first_column;
  64    dst.last_line = last.last_line;
  65    dst.last_column = last.last_column;
  66}
  67*/
  68
  69module.exports = constructionPhase;
  70
  71const NQVAL = {t: "V", s:[1,0], v: [{t:"NQ"}]};
  72
  73function constructionPhase(ctx, srcFile) {
  74
  75    const fullFileName = srcFile;
  76    const fullFilePath = path.dirname(fullFileName);
  77
  78    const src = fs.readFileSync(fullFileName, "utf8");
  79    ctx.ast = parser.parse(src);
  80
  81    assert(ctx.ast.type == "BLOCK");
  82
  83    ctx.lc = new LCAlgebra(ctx.F);
  84    ctx.filePath= fullFilePath;
  85    ctx.fileName= fullFileName;
  86    ctx.includedFiles = {};
  87    ctx.includedFiles[fullFileName] = src.split("\n");
  88
  89    ctx.refs = [];
  90    createRefs(ctx, ctx.ast);
  91    exec(ctx, ctx.ast);
  92
  93}
  94
  95function exec(ctx, ast) {
  96    if (!ast) {
  97        return ctx.throwError(ast, "Null AST");
  98    }
  99    if (ast.type == "NUMBER") {
 100        return execNumber(ctx,ast);
 101    } else if (ast.type == "VARIABLE") {
 102        return execVariable(ctx, ast);
 103    } else if (ast.type == "PIN") {
 104        return execPin(ctx, ast);
 105    } else if (ast.type == "OP") {
 106        if (ast.op == "=") {
 107            return execAssignement(ctx, ast);
 108        } else if (ast.op == "<--") {
 109            return execAssignement(ctx, ast);
 110        } else if (ast.op == "<==") {
 111            return execSignalAssignConstrain(ctx, ast);
 112        } else if (ast.op == "===") {
 113            return execConstrain(ctx, ast);
 114        } else if (ast.op == "+=") {
 115            return execOpOp(ctx, ast, "add", "LEFT");
 116        } else if (ast.op == "*=") {
 117            return execOpOp(ctx, ast, "mul", "LEFT");
 118        } else if (ast.op == "+") {
 119            return execOp(ctx, ast, "add", 2);
 120        } else if (ast.op == "-") {
 121            return execOp(ctx, ast, "sub", 2);
 122        } else if (ast.op == "UMINUS") {
 123            return execOp(ctx, ast, "neg", 1);
 124        } else if (ast.op == "*") {
 125            return execOp(ctx, ast, "mul", 2);
 126        } else if (ast.op == "%") {
 127            return execOp(ctx, ast, "mod", 2);
 128        } else if (ast.op == "PLUSPLUSRIGHT") {
 129            return execOpOp(ctx, ast, "add", "RIGHT");
 130        } else if (ast.op == "PLUSPLUSLEFT") {
 131            return execOpOp(ctx, ast, "add", "LEFT");
 132        } else if (ast.op == "MINUSMINUSRIGHT") {
 133            return execOpOp(ctx, ast, "sub", "RIGHT");
 134        } else if (ast.op == "MINUSMINUSLEFT") {
 135            return execOpOp(ctx, ast, "sub", "LEFT");
 136        } else if (ast.op == "/") {
 137            return execOp(ctx, ast, "div", 2);
 138        } else if (ast.op == "\\") {
 139            return execOp(ctx, ast, "idiv", 2);
 140        } else if (ast.op == "**") {
 141            return execOp(ctx, ast, "pow", 2);
 142        } else if (ast.op == "&") {
 143            return execOp(ctx, ast, "band", 2);
 144        } else if (ast.op == "|") {
 145            return execOp(ctx, ast, "bor", 2);
 146        } else if (ast.op == "^") {
 147            return execOp(ctx, ast, "bxor", 2);
 148        } else if (ast.op == "~") {
 149            return execOp(ctx, ast, "bnot", 1);
 150        } else if (ast.op == "&&") {
 151            return execOp(ctx, ast, "land", 2);
 152        } else if (ast.op == "||") {
 153            return execOp(ctx, ast, "lor", 2);
 154        } else if (ast.op == "!") {
 155            return execOp(ctx, ast, "lnot", 1);
 156        } else if (ast.op == "<<") {
 157            return execOp(ctx, ast, "shl", 2);
 158        } else if (ast.op == ">>") {
 159            return execOp(ctx, ast, "shr", 2);
 160        } else if (ast.op == "<") {
 161            return execOp(ctx, ast, "lt", 2);
 162        } else if (ast.op == ">") {
 163            return execOp(ctx, ast, "gt", 2);
 164        } else if (ast.op == "<=") {
 165            return execOp(ctx, ast, "leq", 2);
 166        } else if (ast.op == ">=") {
 167            return execOp(ctx, ast, "geq", 2);
 168        } else if (ast.op == "==") {
 169            return execOp(ctx, ast, "eq", 2);
 170        } else if (ast.op == "!=") {
 171            return execOp(ctx, ast, "neq", 2);
 172        } else if (ast.op == "?") {
 173            return execTerCon(ctx, ast);
 174        } else {
 175            ctx.throwError(ast, "Invalid operation: " + ast.op);
 176        }
 177    } else if (ast.type == "DECLARE") {
 178        if (ast.declareType == "COMPONENT") {
 179            return execDeclareComponent(ctx, ast);
 180        } else if ((ast.declareType == "SIGNALIN")||
 181                   (ast.declareType == "SIGNALOUT")||
 182                   (ast.declareType == "SIGNAL")) {
 183            return execDeclareSignal(ctx, ast);
 184        } else if (ast.declareType == "VARIABLE") {
 185            return execDeclareVariable(ctx, ast);
 186        } else {
 187            ctx.throwError(ast, "Invalid declaration: " + ast.declareType);
 188        }
 189    } else if (ast.type == "FUNCTIONCALL") {
 190        return execFunctionCall(ctx, ast);
 191    } else if (ast.type == "BLOCK") {
 192        return execBlock(ctx, ast);
 193    } else if (ast.type == "COMPUTE") {
 194        return execCompute(ctx, ast);
 195    } else if (ast.type == "FOR") {
 196        return execLoop(ctx, ast);
 197    } else if (ast.type == "WHILE") {
 198        return execLoop(ctx, ast);
 199    } else if (ast.type == "IF") {
 200        return execIf(ctx, ast);
 201    } else if (ast.type == "RETURN") {
 202        return execReturn(ctx, ast);
 203    } else if (ast.type == "TEMPLATEDEF") {
 204        return execTemplateDef(ctx, ast);
 205    } else if (ast.type == "FUNCTIONDEF") {
 206        return execFunctionDef(ctx, ast);
 207    } else if (ast.type == "INCLUDE") {
 208        return execInclude(ctx, ast);
 209    } else if (ast.type == "ARRAY") {
 210        return execArray(ctx, ast);
 211    } else {
 212        ctx.throwError(ast, "Invalid AST node type: " + ast.type);
 213    }
 214}
 215
 216function execNumber(ctx, ast) {
 217    return {
 218        t: "V",
 219        s:[1,0],
 220        v: [{
 221            t: "N",
 222            v: ctx.F.e(ast.value)
 223        }]
 224    };
 225}
 226
 227
 228function execTemplateDef(ctx, ast) {
 229    ctx.templates[ast.name] = {
 230        block: ast.block,
 231        params: ast.params,
 232        fileName: ctx.fileName,
 233        filePath: ctx.filePath,
 234    };
 235}
 236
 237function execFunctionDef(ctx, ast) {
 238    ctx.functions[ast.name] = {
 239        block: ast.block,
 240        params: ast.params,
 241        fileName: ctx.fileName,
 242        filePath: ctx.filePath
 243    };
 244}
 245
 246function execDeclareComponent(ctx, ast) {
 247    if (ast.name.type != "VARIABLE") return ctx.throwError( ast, "Invalid component name");
 248
 249    const sizes=[];
 250    for (let i=0; i< ast.name.selectors.length; i++) {
 251        const sizeRef = exec(ctx, ast.name.selectors[i]);
 252        if (ctx.error) return;
 253
 254        const size = val(ctx, sizeRef);
 255        if (size.t != "N") return ctx.throwError(  ast.name.selectors[i], "expected a number");
 256
 257        sizes.push( Scalar.toNumber(size.v) );
 258    }
 259
 260    let cIdx = ctx.addComponent(ast.name.name, sizes);
 261    if (!Array.isArray(cIdx)) cIdx = [cIdx, cIdx+1];
 262
 263
 264    ctx.refs[ast.refId].s = utils.accSizes(sizes);
 265    ctx.refs[ast.refId].cIdx = cIdx[0];
 266
 267    return ctx.refs[ast.refId];
 268}
 269
 270
 271function execDeclareSignal(ctx, ast) {
 272
 273    if (ast.name.type != "VARIABLE") return ctx.throwError(ast, "Invalid component name");
 274
 275    const sizes=[];
 276    for (let i=0; i< ast.name.selectors.length; i++) {
 277        const size = exec(ctx, ast.name.selectors[i]);
 278        if (ctx.error) return;
 279        if (size.s[0] != 1) return ctx.throwError(ast, "Size cannot be an array");
 280        if (size.v[0].t != "N") return ctx.throwError(ast, "Size must be declared in construction time");
 281        sizes.push( Scalar.toNumber(size.v[0].v) );
 282    }
 283
 284    let sIdx = ctx.addSignal(ast.name.name, sizes);
 285    if (!Array.isArray(sIdx)) sIdx = [sIdx, sIdx+1];
 286    for (let i=sIdx[0]; i<sIdx[1]; i++) {
 287        ctx.signals[i] = {
 288            o: 0,
 289            e: -1
 290        };
 291
 292        if (ast.declareType == "SIGNALIN") {
 293            ctx.signals[i].o |= ctx.IN;
 294            ctx.components[ctx.currentComponent].nInSignals+=1;
 295        }
 296        if (ast.declareType == "SIGNALOUT") {
 297            ctx.signals[i].o |= ctx.OUT;
 298        }
 299        if (ast.private ) {
 300            ctx.signals[i].o |= ctx.PRV;
 301        }
 302        if (ctx.main) {
 303            ctx.signals[i].o |= ctx.MAIN;
 304        }
 305
 306        // ctx.components[ctx.currentComponent].signals.push(i);
 307    }
 308
 309    const v = ctx.refs[ast.refId];
 310    v.s = utils.accSizes(sizes);
 311    v.sIdx = sIdx[0];
 312
 313    return v;
 314}
 315
 316function execDeclareVariable(ctx, ast) {
 317
 318    if (ast.name.type != "VARIABLE") return ctx.throwError(ast, "Invalid linear combination name");
 319
 320    const sizes=[];
 321    for (let i=0; i< ast.name.selectors.length; i++) {
 322        const size = exec(ctx, ast.name.selectors[i]);
 323        if (ctx.error) return;
 324        if (size.s[0] != 1) return ctx.throwError(ast, "Size cannot be an array");
 325        if (size.v[0].t != "N") return ctx.throwError(ast, "Size must be declared in construction time");
 326        sizes.push( Scalar.toNumber(size.v[0].v) );
 327    }
 328
 329    const v = ctx.refs[ast.refId];
 330
 331    v.s = utils.accSizes(sizes);
 332    v.v = new Array(v.s[0]);
 333
 334    return v;
 335}
 336
 337function execAssignement(ctx, ast) {
 338    let left;
 339    const leftSels=[];
 340    if (ast.values[0].type == "DECLARE") {
 341        left = exec(ctx, ast.values[0]);
 342        if (ctx.error) return;
 343    } else if (ast.values[0].type == "PIN") {
 344        left = execPin(ctx, ast.values[0]);
 345        if (ctx.error) return;
 346    } else {
 347        if (!utils.isDefined(ast.values[0].refId)) return ctx.throwError(ast, "Assigning to a non variable");
 348        left = ctx.refs[ast.values[0].refId];
 349        if (ast.values[0].selectors) {
 350            for (let i=0; i< ast.values[0].selectors.length; i++) {
 351                const sel = exec(ctx, ast.values[0].selectors[i]);
 352                if (ctx.error) return;
 353
 354                if (sel.s[0] != 1) return ctx.throwError(ast, "Selector cannot be an array");
 355                if (sel.v[0].t != "N") return {t: "NQ"};
 356
 357                leftSels.push( Scalar.toNumber(sel.v[0].v) );
 358            }
 359        }
 360
 361    }
 362
 363    if ((!left)||(!left.s)) return ctx.throwError(ast, "variable. not defined yet");
 364
 365    if (left.t == "C") return execInstantiateComponet(ctx, left, ast.values[1], leftSels);
 366    if ((left.t == "S")&&( ["<--", "<==", "-->", "==>"].indexOf(ast.op) < 0)) return ctx.throwError(ast, "Cannot assign to a signal with `=` use <-- or <== ops");
 367    if ((left.t == "V")&&( ["<--", "<==", "-->", "==>"].indexOf(ast.op) >= 0)) return ctx.throwError(ast, `Cannot assign to a var with ${ast.op}. use = op`);
 368
 369    const right = exec(ctx, ast.values[1]);
 370    if (ctx.error) return;
 371
 372    if (!utils.sameSizes(left.s.slice(leftSels.length),right.s)) return ctx.throwError(ast, "Sizes in assignment must be the same");
 373
 374    let o = 0;
 375    for (let i=0; i<leftSels.length; i++) o += leftSels[i]*left.s[i+1];
 376    if (left.t == "V") {
 377        if (right.t=="V") {
 378            for (let i=0; i<right.s[0]; i++) {
 379                left.v[o+i]=right.v[i];
 380            }
 381        } else if (right.t == "S") {
 382            for (let i=0; i<right.s[0]; i++) {
 383                left.v[o+i]={t: "LC", coefs: {}};
 384                left.v[o+i].coefs[right.sIdx+i] = ctx.F.one;
 385            }
 386        }
 387    } else if ( left.t == "S") {
 388        if (right.t=="V") {
 389            for (let i=0; i<right.s[0]; i++) {
 390                const ev = ctx.lc.evaluate(ctx, right.v[i]);
 391                setSignalValue(left.sIdx + o +i, ev);
 392            }
 393        } else if (right.t == "S") {
 394            for (let i=0; i<right.s[0]; i++) {
 395                joinSignals(left.sIdx + o + i, right.sIdx + i);
 396            }
 397        }
 398    }
 399
 400    return right;
 401
 402    function setSignalValue(dSIdx, v) {
 403        let sDest = ctx.signals[dSIdx];
 404        while (sDest.e>=0) sDest=ctx.signals[sDest.e];
 405
 406        if (utils.isDefined(sDest.v)) return ctx.throwError(ast, "Signals cannot be assigned twice");
 407
 408        if (v !== null) {
 409            sDest.v = v;
 410        }
 411    }
 412
 413    function joinSignals(dIdx, sIdx) {
 414        let sDest=ctx.signals[dIdx];
 415        let isOut = (sDest.o & ctx.MAIN)&&(sDest.o & ctx.OUT);
 416        while (sDest.e>=0) {
 417            sDest=ctx.signals[sDest.e];
 418            isOut = isOut || ((sDest.o & ctx.MAIN)&&(sDest.o & ctx.OUT));
 419        }
 420
 421        if (utils.isDefined(sDest.v)) return ctx.throwError(ast, "Signals cannot be assigned twice");
 422
 423        let sSrc = ctx.signals[sIdx];
 424        let isIn  = (sSrc.o & ctx.MAIN)&&(sSrc.o & ctx.IN);
 425        while (sSrc.e>=0) {
 426            sSrc=ctx.signals[sSrc.e];
 427            isIn = isIn || ((sSrc.o & ctx.MAIN)&&(sSrc.o & ctx.IN));
 428        }
 429
 430        // Skip if an out is assigned directly to an input.
 431        if ((!isIn)||(!isOut)) {
 432            sDest.e = sIdx;
 433        } else {
 434            if (utils.isDefined(sSrc.v)) sDest.v = sSrc.v;
 435        }
 436    }
 437}
 438
 439
 440
 441function execInstantiateComponet(ctx, vr, fn, sels) {
 442
 443    if (fn.type != "FUNCTIONCALL") return ctx.throwError(fn, "Right type of instantiate component must be a function call");
 444
 445    const templateName = fn.name;
 446
 447    const template = ctx.templates[templateName];
 448    if (!template) return ctx.throwError(fn, "Invalid Template");
 449
 450    const paramValues = [];
 451    for (let i=0; i< fn.params.length; i++) {
 452        const v = exec(ctx, fn.params[i]);
 453        if (ctx.error) return;
 454        for (let j=0; j<v.s[0]; j++) {
 455            if (v.v[j].t != "N") ctx.throwError(fn, "Parameters of a template must be constant");
 456        }
 457        paramValues.push(v);
 458    }
 459    if (template.params.length != paramValues.length) ctx.throwError(fn, "Invalid Number of parameters");
 460
 461    let o=0;
 462    for (let i=0; i<sels.length; i++) o += sels[i] * vr.s[i+1];
 463    for (let i=o; i<o+vr.s[sels.length]; i++) {
 464        instantiateComponent(vr.cIdx+i);
 465    }
 466
 467    function instantiateComponent(cIdx) {
 468
 469        function extractValue(sizes, arr) {
 470            if (sizes[0] == 1) return arr[0].v.toString();
 471            const res = [];
 472            for (let i=0; i<sizes[0]; i += sizes[1]) {
 473                res.push(extractValue(sizes.slice(1), arr.slice(i, i+sizes[1])));
 474            }
 475            return res;
 476        }
 477
 478        if (ctx.components[cIdx]) return ctx.throwError(fn, "Component already instantiated");
 479
 480        const oldComponent = ctx.currentComponent;
 481        const oldFileName = ctx.fileName;
 482        const oldFilePath = ctx.filePath;
 483        const oldMain = ctx.main;
 484        const oldRefs = ctx.refs;
 485
 486        if (ctx.currentComponent==-1) {
 487            ctx.main=true;
 488        } else {
 489            ctx.main=false;
 490        }
 491
 492        ctx.currentComponent = cIdx;
 493
 494        ctx.components[cIdx] = {
 495            params: {},
 496            names: ctx.newTableName(),
 497            nInSignals: 0,
 498            template: templateName
 499        };
 500
 501
 502        if (template.params.length != paramValues.length) return ctx.throwError(fn, "Invalid number of parameters: " + templateName);
 503        ctx.refs = [];
 504
 505        const scope = {};
 506        for (let i=0; i< template.params.length; i++) {
 507            ctx.refs.push({
 508                t: "V",
 509                s: paramValues[i].s,
 510                v: paramValues[i].v,
 511                refId: i
 512            });
 513            scope[template.params[i]] = i;
 514            ctx.components[cIdx].params[template.params[i]] = extractValue(paramValues[i].s, paramValues[i].v);
 515        }
 516
 517        createRefs(ctx, template.block, scope);
 518
 519        ctx.fileName = template.fileName;
 520        ctx.filePath = template.filePath;
 521
 522        execBlock(ctx, template.block);
 523
 524        ctx.refs = oldRefs;
 525        ctx.fileName = oldFileName;
 526        ctx.filePath = oldFilePath;
 527        ctx.currentComponent = oldComponent;
 528        ctx.main = oldMain;
 529    }
 530}
 531
 532
 533function execBlock(ctx,  ast) {
 534    for (let i=0; i<ast.statements.length; i++) {
 535        exec(ctx, ast.statements[i]);
 536        if (ctx.returnValue) return;
 537        if (ctx.error) return;
 538    }
 539}
 540
 541function clone(a) {
 542    const res = {
 543        t: a.t,
 544        s: a.s
 545    };
 546    if (a.t == "V") {
 547        res.v = new Array(a.v.length);
 548        for (let i=0; i<a.v.length; i++) res.v[i] = a.v[i];
 549    } else if (a.t == "S") {
 550        res.sIdx = a.sIdx;
 551    } else if (a.t == "C") {
 552        res.cIdx = a.cIdx;
 553    }
 554    return res;
 555}
 556
 557function execFunctionCall(ctx, ast) {
 558
 559    if (ast.name == "log") {
 560        const v = exec(ctx, ast.params[0]);
 561        const ev = val(ctx, v, ast);
 562        if (ev.v) {
 563            console.log(ev.v.toString());
 564        } else {
 565            console.log(JSON.stringify(stringifyBigInts(ev)));
 566        }
 567        return;
 568    }
 569    if (ast.name == "assert") {
 570        ast.fileName = ctx.fileName;
 571        ast.filePath = ctx.filePath;
 572
 573        const v = exec(ctx, ast.params[0]);
 574        const ev = val(ctx, v, ast);
 575        if ((typeof ev.v !== "undefined")&&(ctx.F.isZero(ev.v))) return ctx.throwError(ast, "Assertion failed");
 576        return;
 577    }
 578
 579    const fnc = ctx.functions[ast.name];
 580
 581    if (!fnc) return ctx.throwError(ast, "Function not defined");
 582
 583    const paramValues = [];
 584    for (let i=0; i< ast.params.length; i++) {
 585        const v = exec(ctx, ast.params[i]);
 586        if (ctx.error) return;
 587
 588        paramValues.push(v);
 589    }
 590
 591    if (ast.params.length != paramValues.length) ctx.throwError(ast, "Invalid Number of parameters");
 592
 593    const oldFileName = ctx.fileName;
 594    const oldFilePath = ctx.filePath;
 595    const oldRefs = ctx.refs;
 596
 597    ctx.fileName = fnc.fileName;
 598    ctx.filePath = fnc.filePath;
 599    ctx.refs = [];
 600
 601    const scope = {};
 602    for (let i=0; i< fnc.params.length; i++) {
 603        const entry = clone(paramValues[i]);
 604        entry.refId = i;
 605        scope[fnc.params[i]] = i;
 606        ctx.refs.push(entry);
 607    }
 608
 609    createRefs(ctx, fnc.block, scope);
 610
 611    execBlock(ctx, fnc.block);
 612
 613    const res = ctx.returnValue;
 614    ctx.returnValue = null;
 615
 616    ctx.fileName = oldFileName;
 617    ctx.filePath = oldFilePath;
 618    ctx.refs = oldRefs;
 619
 620    return res;
 621}
 622
 623function execReturn(ctx, ast) {
 624    ctx.returnValue = exec(ctx, ast.value);
 625    return;
 626}
 627
 628
 629function execVariable(ctx, ast) {
 630
 631    const v = ctx.refs[ast.refId];
 632    if (!v) {
 633        return ctx.throwError(ast, "Variable not defined: "+ast.name);
 634    }
 635
 636    const sels = [];
 637    for (let i=0; i< ast.selectors.length; i++) {
 638        const sel = exec(ctx, ast.selectors[i]);
 639        if (ctx.error) return;
 640        if (sel.s[0] != 1) return ctx.throwError(ast, "Variable selector cannot be an array");
 641        if (sel.v[0].t != "N") return NQVAL;
 642        sels.push(Scalar.toNumber(sel.v[0].v));
 643    }
 644
 645    let o = 0;
 646    let s = v.s[0];
 647
 648    if (sels.length > v.s.length-2) return ctx.throwError(ast, "Too many selectors");
 649    for (let i=0; i<sels.length; i++) {
 650        if (sels[i] >= v.s[i] / v.s[i+1]) return ctx.throwError(ast, "Out of Range");
 651        if (sels[i] < 0 ) return ctx.throwError(ast, "selector negative");
 652        o += sels[i] * v.s[i+1];
 653        s = v.s[i+1];
 654    }
 655
 656    if (v.t == "V") {
 657        return {
 658            t: "V",
 659            s: v.s.slice(sels.length),
 660            v: v.v.slice(o, o+s)
 661        };
 662    } else if (v.t == "S") {
 663        return {
 664            t: "S",
 665            s: v.s.slice(sels.length),
 666            sIdx: o + v.sIdx
 667        };
 668    } else if (v.t == "C") {
 669        return {
 670            t: "C",
 671            s: v.s.slice(sels.length),
 672            cIdx: o
 673        };
 674    } else {
 675        assert(false);
 676    }
 677}
 678
 679
 680
 681
 682function execPin(ctx, ast) {
 683    const selsC = [];
 684    for (let i=0; i< ast.component.selectors.length; i++) {
 685        const sel = exec(ctx, ast.component.selectors[i]);
 686        if (ctx.error) return;
 687
 688        if (sel.s[0] != 1) return ctx.throwError(ast, "Component selector cannot be an array");
 689        if (sel.v[0].t != "N") return NQVAL;
 690        selsC.push(Scalar.toNumber(sel.v[0].v));
 691    }
 692
 693    const cIdx = ctx.getComponentIdx(ast.component.name, selsC);
 694    if (cIdx<0) return ctx.throwError(ast.component, "Component does not exists: "+ast.component.name);
 695
 696    const selsP = [];
 697    for (let i=0; i< ast.pin.selectors.length; i++) {
 698        const sel = exec(ctx, ast.pin.selectors[i]);
 699        if (ctx.error) return;
 700        if (sel.s[0] != 1) return ctx.throwError(ast, "Signal selector cannot be an array");
 701        if (sel.v[0].t != "N") return NQVAL;
 702        selsP.push(Scalar.toNumber(sel.v[0].v));
 703    }
 704    const sIdx = ctx.components[cIdx].names.getSignalIdx(ast.pin.name, selsP);
 705
 706    if (sIdx<0) return ctx.throwError(ast, "Signal not defined:" + buildFullName() );
 707    return {
 708        t: "S",
 709        sIdx: sIdx,
 710        s: utils.accSizes(ctx.components[cIdx].names.o[ast.pin.name].sizes).slice(selsP.length)
 711    };
 712
 713    function buildFullName() {
 714        return ast.component.name + sels2str(selsC) + "." + ast.pin.name + sels2str(selsP);
 715    }
 716
 717    function sels2str(sels) {
 718        let S = "";
 719        for (let i=0; i< sels.length; i++) {
 720            S += "[" + sels[i] + "]";
 721        }
 722        return S;
 723    }
 724}
 725
 726function execLoop(ctx, ast) {
 727
 728    if (ast.init) {
 729        exec(ctx, ast.init);
 730        if (ctx.error) return;
 731    }
 732
 733    let v = exec(ctx, ast.condition);
 734    if (ctx.error) return;
 735    if (v.s[0] != 1) return ctx.throwError(ast.condition, "Condition in loop cannot be an array");
 736    if (v.v[0].t != "N") {
 737        iterateAST(ast, (ast) => {
 738            if (ast.type == "OP") {
 739                if (["==>", "<==", "==="].indexOf(ast.op) >= 0) {
 740                    return ctx.throwError(ast.condition, "Constraint inside a calculating block");
 741                }
 742            }
 743        });
 744
 745        // Assert no constraints
 746        return;
 747    }
 748
 749    while ((! ctx.F.isZero(v.v[0].v))&&(!ctx.returnValue)) {
 750        exec(ctx, ast.body);
 751        if (ctx.error) return;
 752
 753        if (ast.step) {
 754            exec(ctx, ast.step);
 755            if (ctx.error) return;
 756        }
 757
 758        v = exec(ctx, ast.condition);
 759        if (ctx.error) return;
 760        if (v.s[0] != 1) return ctx.throwError(ast.condition, "Condition in loop cannot be an array");
 761        if (v.v[0].t != "N") return ctx.throwError(ast.condition, "Condition result not a number");
 762    }
 763}
 764
 765function execCompute(ctx, ast) {
 766    iterateAST(ast, (ast) => {
 767        if (ast.type == "OP") {
 768            if (["==>", "<==", "==="].indexOf(ast.op) >= 0) {
 769                return ctx.throwError(ast.condition, "Constraint inside a calculating block");
 770            }
 771        }
 772    });
 773}
 774
 775function execIf(ctx, ast) {
 776    let v = exec(ctx, ast.condition);
 777    if (ctx.error) return;
 778    if (v.s[0] != 1) return ctx.throwError(ast.condition, "Condition cannot be an array");
 779    if (v.v[0].t != "N") {
 780        iterateAST(ast, (ast) => {
 781            if (ast.type == "OP") {
 782                if (["==>", "<==", "==="].indexOf(ast.op) >= 0) {
 783                    return ctx.throwError(ast.condition, "Constraint inside a calculating block");
 784                }
 785            }
 786        });
 787
 788        // Assert no constraints
 789        return;
 790    }
 791
 792    if (!ctx.F.isZero(v.v[0].v)) {
 793        exec(ctx, ast.then);
 794    } else {
 795        if (ast.else) {
 796            exec(ctx, ast.else);
 797        }
 798    }
 799}
 800
 801
 802function execTerCon(ctx, ast) {
 803    let v = exec(ctx, ast.values[0]);
 804    if (ctx.error) return;
 805    if (v.s[0] != 1) return ctx.throwError(ast.condition, "Condition cannot be an array");
 806    if (v.v[0].t != "N") {
 807        iterateAST(ast, (ast) => {
 808            if (ast.type == "OP") {
 809                if (["==>", "<==", "==="].indexOf(ast.op) >= 0) {
 810                    return ctx.throwError(ast.condition, "Constraint inside a calculating block");
 811                }
 812            }
 813        });
 814
 815        // Assert no constraints
 816        return NQVAL;
 817    }
 818
 819    if (!ctx.F.isZero(v.v[0].v)) {
 820        return exec(ctx, ast.values[1]);
 821    } else {
 822        return exec(ctx, ast.values[2]);
 823    }
 824}
 825
 826function execOp(ctx, ast, op, nOps) {
 827    const operands = [];
 828    for (let i=0; i<nOps; i++) {
 829        const a = exec(ctx, ast.values[i]);
 830        if (ctx.error) return;
 831        const aV = val(ctx, a, ast.values[i]);
 832        operands.push(aV);
 833    }
 834
 835    return  {
 836        t: "V",
 837        s: [1,0],
 838        v: [ctx.lc[op](...operands)]
 839    };
 840}
 841
 842
 843function execOpOp(ctx, ast, op, lr) {
 844
 845    if (ast.values[0].type != "VARIABLE") return ctx.throwError(ast, "incrementing a non variable");
 846    if (!utils.isDefined(ast.values[0].refId)) return ctx.throwError(ast, "Assigning to a non variable");
 847    const left = ctx.refs[ast.values[0].refId];
 848    if (left.t != "V") return ctx.throwError(ast, `Only variables can use ${ast.op} operations`);
 849    let leftSels = [];
 850    if (ast.values[0].selectors) {
 851        for (let i=0; i< ast.values[0].selectors.length; i++) {
 852            const sel = exec(ctx, ast.values[0].selectors[i]);
 853            if (ctx.error) return;
 854
 855            if (sel.s[0] != 1) return ctx.throwError(ast, "Selector cannot be an array");
 856            if (sel.v[0].t != "N") return {t: "NQ"};
 857
 858            leftSels.push( Scalar.toNumber(sel.v[0].v) );
 859        }
 860    }
 861    if (!left.s) return ctx.throwError(ast, "variable. not defined yet");
 862
 863    let o = 0;
 864    let s = left.s[0];
 865    for (let i=0; i<leftSels.length; i++) {
 866        o += leftSels[i]*left.s[i+1];
 867        s = left.s[i+1];
 868    }
 869    if (s != 1) return ctx.throwError(ast, `invalid operator ${ast.op} in an array`);
 870    const resBefore = left.v[o];
 871
 872    let right;
 873    if (ast.values[1]) {
 874        const rightRef = exec(ctx, ast.values[1]);
 875        if (ctx.error) return;
 876        right = val(ctx, rightRef);
 877    } else {
 878        right = {t:"N", v: ctx.F.one};
 879    }
 880
 881    if (!right) return ctx.throwError(ast, "adding a no number");
 882
 883    const resAfter = ctx.lc[op](resBefore, right);
 884    left.v[o] = resAfter;
 885
 886    if (lr == "RIGHT") {
 887        return {
 888            t: "V",
 889            s: [1,0],
 890            v: [resBefore]
 891        };
 892    } else {
 893        return {
 894            t: "V",
 895            s: [1,0],
 896            v: [resAfter]
 897        };
 898    }
 899}
 900
 901// Extract the value of a reference.
 902// Must be a single value. (cannot be an array)
 903// The result is an LCAlgebra compatible object {t: ["N"|"LC"|"QEX"|"NQ"], [coefs|v|a,b,c]}
 904function val(ctx, a, ast) {
 905    if (a.s[0] != 1) return ctx.throwError(ast, "Array not allowed");
 906    if (a.t=="V") {
 907        return a.v[0];
 908    } else if (a.t=="S") {
 909        const res = {
 910            t: "LC",
 911            coefs: {}
 912        };
 913        let sIdx = a.sIdx;
 914        while (ctx.signals[sIdx].e >= 0) sIdx = ctx.signals[sIdx].e;
 915        res.coefs[sIdx] = ctx.F.one;
 916        return res;
 917    } else {
 918        ctx.throwError(ast, "Invalid type: " + a.t);
 919    }
 920}
 921
 922function execConstrain(ctx, ast) {
 923    ast.fileName = ctx.fileName;
 924    ast.filePath = ctx.filePath;
 925    const a = exec(ctx, ast.values[0]);
 926    if (ctx.error) return;
 927    const aV = val(ctx, a, ast.values[0]);
 928    const b = exec(ctx, ast.values[1]);
 929    if (ctx.error) return;
 930    const bV = val(ctx, b, ast.values[1]);
 931
 932    const res = ctx.lc.sub(aV,bV);
 933    if (res.type == "NQ") return ctx.throwError(ast, "Non Quadratic constraint");
 934
 935    if (!ctx.lc.isZero(res)) {
 936        ctx.constraints.push(ctx.lc.toQEX(res));
 937        if (ctx.verbose) {
 938            if ((ctx.constraints.length % 10000 == 0)&&(ctx.constraints.length>0)) console.log("Constraints: " + ctx.constraints.length);
 939        }
 940    }
 941
 942    return a;
 943}
 944
 945function execSignalAssignConstrain(ctx, ast) {
 946    const v = execAssignement(ctx,ast);
 947    if (ctx.error) return;
 948    execConstrain(ctx, ast);
 949    if (ctx.error) return;
 950    return v;
 951}
 952
 953function execInclude(ctx, ast) {
 954    if (ast.block) {
 955
 956        const oldFilePath = ctx.filePath;
 957        const oldFileName = ctx.fileName;
 958
 959        ctx.filePath = ast.filePath;
 960        ctx.fileName = ast.fileName;
 961
 962        exec(ctx, ast.block);  // Process only one.
 963
 964        ctx.filePath = oldFilePath;
 965        ctx.fileName = oldFileName;
 966    }
 967}
 968
 969function execArray(ctx, ast) {
 970    const res = {t: "V", v:[]};
 971
 972
 973    let subSize = null;
 974
 975    for (let i=0; i<ast.values.length; i++) {
 976        const e = exec(ctx, ast.values[i]);
 977        if (i==0) {
 978            subSize=e.s;
 979        } else {
 980            if (!utils.sameSizes(subSize, e.s)) return ctx.throwError(ast, "Array mus be homogeneous");
 981        }
 982        if (e.t == "V") {
 983            for (let j=0; j<e.v.length;j++) res.v.push(e.v[j]);
 984        } else if (e.t == "S") {
 985            for (let j=0; j<e.v.length;j++) {
 986                const sv = {t: "LC", coefs: {}};
 987                sv.coefs[e.sIdx+j] = ctx.F.one;
 988                res.v.push(sv);
 989            }
 990        } else {
 991            return ctx.throwError(ast, "Type not allowed in array");
 992        }
 993    }
 994
 995    res.s = [ast.values.length*subSize[0], ...subSize];
 996
 997    return res;
 998}
 999
1000function createRefs(ctx, ast, scope) {
1001    const scopeLabels = [];
1002    const scopes = scope ? [scope] : [{}];
1003    iterateAST(ast, (ast, level) => {
1004        while ((scopeLabels.length>0)&&(!level.startsWith(scopeLabels[scopeLabels.length-1]))) {
1005            scopes.pop();
1006            scopeLabels.pop();
1007        }
1008        if (ast.type == "DECLARE") {
1009            if (ast.declareType == "COMPONENT") {
1010                ast.refId = define(ast.name.name, {t: "C"});
1011            } else if ((ast.declareType == "SIGNALIN")||
1012                       (ast.declareType == "SIGNALOUT")||
1013                       (ast.declareType == "SIGNAL")) {
1014                ast.refId = define(ast.name.name, {t: "S"});
1015            } else if (ast.declareType == "VARIABLE") {
1016                ast.refId = define(ast.name.name, {t: "V", s: null});
1017            } else {
1018                return ctx.throwError(ast, "Invalid declaration: " + ast.declareType);
1019            }
1020        } else if (["BLOCK", "FOR", "IF", "WHILE", "COMPUTE"].indexOf(ast.type) >= 0) {
1021            scopeLabels.push(level);
1022            scopes.push({});
1023        } else if (ast.type == "TEMPLATEDEF") {
1024            ast.refId = define(ast.name, {t: "T"});
1025        } else if (ast.type == "FUNCTIONDEF") {
1026            ast.refId = define(ast.name, {t: "F"});
1027        } else if (ast.type == "INCLUDE") {
1028            const incFileName = path.resolve(ctx.filePath, ast.file);
1029            const incFilePath = path.dirname(incFileName);
1030
1031            ctx.includedFiles = ctx.includedFiles || [];
1032            if (ctx.includedFiles[incFileName]) {
1033                ctx.bloc = null; // Include already included...
1034                return;
1035            }
1036
1037            const src = fs.readFileSync(incFileName, "utf8");
1038
1039            if (!src) return ctx.throwError(ast, "Include file not found: "+incFileName);
1040
1041            ctx.includedFiles[incFileName] = src.split("\n");
1042
1043            ast.block = parser.parse(src);
1044            ast.filePath = incFilePath;
1045            ast.fileName = incFileName;
1046
1047            const oldFilePath = ctx.filePath;
1048            const oldFileName = ctx.fileName;
1049            ctx.filePath = incFilePath;
1050            ctx.fileName = incFileName;
1051
1052            scopes.push({});
1053            createRefs(ctx, ast.block);
1054            scopes.pop();
1055
1056            ctx.filePath = oldFilePath;
1057            ctx.fileName = oldFileName;
1058        } else if (ast.type == "VARIABLE") {
1059            ast.refId = name2ref(ast.name);
1060        }
1061
1062        function define(name, entry) {
1063            if (scopes[scopes.length-1][name]) return ctx.throwError(ast, `Name already defined: ${name}`);
1064            for (let i=scopes.length-2; i>=0; i--) {
1065                if (scopes[i][name]) ctx.logWarning(ast, `Shadowing variable: ${name}`);
1066            }
1067            const refId = ctx.refs.length;
1068            entry.refId = refId;
1069            ctx.refs.push(entry);
1070            scopes[scopes.length-1][name] = refId;
1071            return refId;
1072        }
1073
1074        function name2ref(n) {
1075            for (let i=scopes.length-1; i>=0; i--) {
1076                if (typeof scopes[i][n] !== "undefined") return scopes[i][n];
1077            }
1078            return -1;
1079        }
1080
1081
1082    });
1083
1084}
1085
1086