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        const v = exec(ctx, ast.params[0]);
 571        const ev = val(ctx, v, ast);
 572        if (ctx.F.isZero(ev)) return ctx.throwError(ast, "Assertion failed");
 573    }
 574
 575    const fnc = ctx.functions[ast.name];
 576
 577    if (!fnc) return ctx.throwError("Function not defined");
 578
 579    const paramValues = [];
 580    for (let i=0; i< ast.params.length; i++) {
 581        const v = exec(ctx, ast.params[i]);
 582        if (ctx.error) return;
 583
 584        paramValues.push(v);
 585    }
 586
 587    if (ast.params.length != paramValues.length) ctx.throwError(ast, "Invalid Number of parameters");
 588
 589    const oldFileName = ctx.fileName;
 590    const oldFilePath = ctx.filePath;
 591    const oldRefs = ctx.refs;
 592
 593    ctx.fileName = fnc.fileName;
 594    ctx.filePath = fnc.filePath;
 595    ctx.refs = [];
 596
 597    const scope = {};
 598    for (let i=0; i< fnc.params.length; i++) {
 599        const entry = clone(paramValues[i]);
 600        entry.refId = i;
 601        scope[fnc.params[i]] = i;
 602        ctx.refs.push(entry);
 603    }
 604
 605    createRefs(ctx, fnc.block, scope);
 606
 607    execBlock(ctx, fnc.block);
 608
 609    const res = ctx.returnValue;
 610    ctx.returnValue = null;
 611
 612    ctx.fileName = oldFileName;
 613    ctx.filePath = oldFilePath;
 614    ctx.refs = oldRefs;
 615
 616    return res;
 617}
 618
 619function execReturn(ctx, ast) {
 620    ctx.returnValue = exec(ctx, ast.value);
 621    return;
 622}
 623
 624
 625function execVariable(ctx, ast) {
 626
 627    const v = ctx.refs[ast.refId];
 628    if (!v) {
 629        return ctx.throwError(ast, "Variable not defined: "+ast.name);
 630    }
 631
 632    const sels = [];
 633    for (let i=0; i< ast.selectors.length; i++) {
 634        const sel = exec(ctx, ast.selectors[i]);
 635        if (ctx.error) return;
 636        if (sel.s[0] != 1) return ctx.throwError(ast, "Variable selector cannot be an array");
 637        if (sel.v[0].t != "N") return NQVAL;
 638        sels.push(Scalar.toNumber(sel.v[0].v));
 639    }
 640
 641    let o = 0;
 642    let s = v.s[0];
 643
 644    if (sels.length > v.s.length-2) return ctx.throwError(ast, "Too many selectors");
 645    for (let i=0; i<sels.length; i++) {
 646        if (sels[i] >= v.s[i] / v.s[i+1]) return ctx.throwError(ast, "Out of Range");
 647        if (sels[i] < 0 ) return ctx.throwError(ast, "selector negative");
 648        o += sels[i] * v.s[i+1];
 649        s = v.s[i+1];
 650    }
 651
 652    if (v.t == "V") {
 653        return {
 654            t: "V",
 655            s: v.s.slice(sels.length),
 656            v: v.v.slice(o, o+s)
 657        };
 658    } else if (v.t == "S") {
 659        return {
 660            t: "S",
 661            s: v.s.slice(sels.length),
 662            sIdx: o + v.sIdx
 663        };
 664    } else if (v.t == "C") {
 665        return {
 666            t: "C",
 667            s: v.s.slice(sels.length),
 668            cIdx: o
 669        };
 670    } else {
 671        assert(false);
 672    }
 673}
 674
 675
 676
 677
 678function execPin(ctx, ast) {
 679    const selsC = [];
 680    for (let i=0; i< ast.component.selectors.length; i++) {
 681        const sel = exec(ctx, ast.component.selectors[i]);
 682        if (ctx.error) return;
 683
 684        if (sel.s[0] != 1) return ctx.throwError(ast, "Component selector cannot be an array");
 685        if (sel.v[0].t != "N") return NQVAL;
 686        selsC.push(Scalar.toNumber(sel.v[0].v));
 687    }
 688
 689    const cIdx = ctx.getComponentIdx(ast.component.name, selsC);
 690    if (cIdx<0) return ctx.throwError(ast.component, "Component does not exists: "+ast.component.name);
 691
 692    const selsP = [];
 693    for (let i=0; i< ast.pin.selectors.length; i++) {
 694        const sel = exec(ctx, ast.pin.selectors[i]);
 695        if (ctx.error) return;
 696        if (sel.s[0] != 1) return ctx.throwError(ast, "Signal selector cannot be an array");
 697        if (sel.v[0].t != "N") return NQVAL;
 698        selsP.push(Scalar.toNumber(sel.v[0].v));
 699    }
 700    const sIdx = ctx.components[cIdx].names.getSignalIdx(ast.pin.name, selsP);
 701
 702    if (sIdx<0) return ctx.throwError(ast, "Signal not defined:" + buildFullName() );
 703    return {
 704        t: "S",
 705        sIdx: sIdx,
 706        s: utils.accSizes(ctx.components[cIdx].names.o[ast.pin.name].sizes).slice(selsP.length)
 707    };
 708
 709    function buildFullName() {
 710        return ast.component.name + sels2str(selsC) + "." + ast.pin.name + sels2str(selsP);
 711    }
 712
 713    function sels2str(sels) {
 714        let S = "";
 715        for (let i=0; i< sels.length; i++) {
 716            S += "[" + sels[i] + "]";
 717        }
 718        return S;
 719    }
 720}
 721
 722function execLoop(ctx, ast) {
 723
 724    if (ast.init) {
 725        exec(ctx, ast.init);
 726        if (ctx.error) return;
 727    }
 728
 729    let v = exec(ctx, ast.condition);
 730    if (ctx.error) return;
 731    if (v.s[0] != 1) return ctx.throwError(ast.condition, "Condition in loop cannot be an array");
 732    if (v.v[0].t != "N") {
 733        iterateAST(ast, (ast) => {
 734            if (ast.type == "OP") {
 735                if (["==>", "<==", "==="].indexOf(ast.op) >= 0) {
 736                    return ctx.throwError(ast.condition, "Constraint inside a calculating block");
 737                }
 738            }
 739        });
 740
 741        // Assert no constraints
 742        return;
 743    }
 744
 745    while ((! ctx.F.isZero(v.v[0].v))&&(!ctx.returnValue)) {
 746        exec(ctx, ast.body);
 747        if (ctx.error) return;
 748
 749        if (ast.step) {
 750            exec(ctx, ast.step);
 751            if (ctx.error) return;
 752        }
 753
 754        v = exec(ctx, ast.condition);
 755        if (ctx.error) return;
 756        if (v.s[0] != 1) return ctx.throwError(ast.condition, "Condition in loop cannot be an array");
 757        if (v.v[0].t != "N") return ctx.throwError(ast.condition, "Condition result not a number");
 758    }
 759}
 760
 761function execCompute(ctx, ast) {
 762    iterateAST(ast, (ast) => {
 763        if (ast.type == "OP") {
 764            if (["==>", "<==", "==="].indexOf(ast.op) >= 0) {
 765                return ctx.throwError(ast.condition, "Constraint inside a calculating block");
 766            }
 767        }
 768    });
 769}
 770
 771function execIf(ctx, ast) {
 772    let v = exec(ctx, ast.condition);
 773    if (ctx.error) return;
 774    if (v.s[0] != 1) return ctx.throwError(ast.condition, "Condition cannot be an array");
 775    if (v.v[0].t != "N") {
 776        iterateAST(ast, (ast) => {
 777            if (ast.type == "OP") {
 778                if (["==>", "<==", "==="].indexOf(ast.op) >= 0) {
 779                    return ctx.throwError(ast.condition, "Constraint inside a calculating block");
 780                }
 781            }
 782        });
 783
 784        // Assert no constraints
 785        return;
 786    }
 787
 788    if (!ctx.F.isZero(v.v[0].v)) {
 789        exec(ctx, ast.then);
 790    } else {
 791        if (ast.else) {
 792            exec(ctx, ast.else);
 793        }
 794    }
 795}
 796
 797
 798function execTerCon(ctx, ast) {
 799    let v = exec(ctx, ast.values[0]);
 800    if (ctx.error) return;
 801    if (v.s[0] != 1) return ctx.throwError(ast.condition, "Condition cannot be an array");
 802    if (v.v[0].t != "N") {
 803        iterateAST(ast, (ast) => {
 804            if (ast.type == "OP") {
 805                if (["==>", "<==", "==="].indexOf(ast.op) >= 0) {
 806                    return ctx.throwError(ast.condition, "Constraint inside a calculating block");
 807                }
 808            }
 809        });
 810
 811        // Assert no constraints
 812        return NQVAL;
 813    }
 814
 815    if (!ctx.F.isZero(v.v[0].v)) {
 816        return exec(ctx, ast.values[1]);
 817    } else {
 818        return exec(ctx, ast.values[2]);
 819    }
 820}
 821
 822function execOp(ctx, ast, op, nOps) {
 823    const operands = [];
 824    for (let i=0; i<nOps; i++) {
 825        const a = exec(ctx, ast.values[i]);
 826        if (ctx.error) return;
 827        const aV = val(ctx, a, ast.values[i]);
 828        operands.push(aV);
 829    }
 830
 831    return  {
 832        t: "V",
 833        s: [1,0],
 834        v: [ctx.lc[op](...operands)]
 835    };
 836}
 837
 838
 839function execOpOp(ctx, ast, op, lr) {
 840
 841    if (ast.values[0].type != "VARIABLE") return ctx.throwError(ast, "incrementing a non variable");
 842    if (!utils.isDefined(ast.values[0].refId)) return ctx.throwError(ast, "Assigning to a non variable");
 843    const left = ctx.refs[ast.values[0].refId];
 844    if (left.t != "V") return ctx.throwError(ast, `Only variables can use ${ast.op} operations`);
 845    let leftSels = [];
 846    if (ast.values[0].selectors) {
 847        for (let i=0; i< ast.values[0].selectors.length; i++) {
 848            const sel = exec(ctx, ast.values[0].selectors[i]);
 849            if (ctx.error) return;
 850
 851            if (sel.s[0] != 1) return ctx.throwError(ast, "Selector cannot be an array");
 852            if (sel.v[0].t != "N") return {t: "NQ"};
 853
 854            leftSels.push( Scalar.toNumber(sel.v[0].v) );
 855        }
 856    }
 857    if (!left.s) return ctx.throwError(ast, "variable. not defined yet");
 858
 859    let o = 0;
 860    let s = left.s[0];
 861    for (let i=0; i<leftSels.length; i++) {
 862        o += leftSels[i]*left.s[i+1];
 863        s = left.s[i+1];
 864    }
 865    if (s != 1) return ctx.throwError(ast, `invalid operator ${ast.op} in an array`);
 866    const resBefore = left.v[o];
 867
 868    let right;
 869    if (ast.values[1]) {
 870        const rightRef = exec(ctx, ast.values[1]);
 871        if (ctx.error) return;
 872        right = val(ctx, rightRef);
 873    } else {
 874        right = {t:"N", v: ctx.F.one};
 875    }
 876
 877    if (!right) return ctx.throwError(ast, "adding a no number");
 878
 879    const resAfter = ctx.lc[op](resBefore, right);
 880    left.v[o] = resAfter;
 881
 882    if (lr == "RIGHT") {
 883        return {
 884            t: "V",
 885            s: [1,0],
 886            v: [resBefore]
 887        };
 888    } else {
 889        return {
 890            t: "V",
 891            s: [1,0],
 892            v: [resAfter]
 893        };
 894    }
 895}
 896
 897// Extract the value of a reference.
 898// Must be a single value. (cannot be an array)
 899// The result is an LCAlgebra compatible object {t: ["N"|"LC"|"QEX"|"NQ"], [coefs|v|a,b,c]}
 900function val(ctx, a, ast) {
 901    if (a.s[0] != 1) return ctx.throwError(ast, "Array not allowed");
 902    if (a.t=="V") {
 903        return a.v[0];
 904    } else if (a.t=="S") {
 905        const res = {
 906            t: "LC",
 907            coefs: {}
 908        };
 909        let sIdx = a.sIdx;
 910        while (ctx.signals[sIdx].e >= 0) sIdx = ctx.signals[sIdx].e;
 911        res.coefs[sIdx] = ctx.F.one;
 912        return res;
 913    } else {
 914        ctx.throwError(ast, "Invalid type: " + a.t);
 915    }
 916}
 917
 918function execConstrain(ctx, ast) {
 919    ast.fileName = ctx.fileName;
 920    ast.filePath = ctx.filePath;
 921    const a = exec(ctx, ast.values[0]);
 922    if (ctx.error) return;
 923    const aV = val(ctx, a, ast.values[0]);
 924    const b = exec(ctx, ast.values[1]);
 925    if (ctx.error) return;
 926    const bV = val(ctx, b, ast.values[1]);
 927
 928    const res = ctx.lc.sub(aV,bV);
 929    if (res.type == "NQ") return ctx.throwError(ast, "Non Quadratic constraint");
 930
 931    if (!ctx.lc.isZero(res)) {
 932        ctx.constraints.push(ctx.lc.toQEX(res));
 933        if (ctx.verbose) {
 934            if ((ctx.constraints.length % 10000 == 0)&&(ctx.constraints.length>0)) console.log("Constraints: " + ctx.constraints.length);
 935        }
 936    }
 937
 938    return a;
 939}
 940
 941function execSignalAssignConstrain(ctx, ast) {
 942    const v = execAssignement(ctx,ast);
 943    if (ctx.error) return;
 944    execConstrain(ctx, ast);
 945    if (ctx.error) return;
 946    return v;
 947}
 948
 949function execInclude(ctx, ast) {
 950    if (ast.block) {
 951
 952        const oldFilePath = ctx.filePath;
 953        const oldFileName = ctx.fileName;
 954
 955        ctx.filePath = ast.filePath;
 956        ctx.fileName = ast.fileName;
 957
 958        exec(ctx, ast.block);  // Process only one.
 959
 960        ctx.filePath = oldFilePath;
 961        ctx.fileName = oldFileName;
 962    }
 963}
 964
 965function execArray(ctx, ast) {
 966    const res = {t: "V", v:[]};
 967
 968
 969    let subSize = null;
 970
 971    for (let i=0; i<ast.values.length; i++) {
 972        const e = exec(ctx, ast.values[i]);
 973        if (i==0) {
 974            subSize=e.s;
 975        } else {
 976            if (!utils.sameSizes(subSize, e.s)) return ctx.throwError(ast, "Array mus be homogeneous");
 977        }
 978        if (e.t == "V") {
 979            for (let j=0; j<e.v.length;j++) res.v.push(e.v[j]);
 980        } else if (e.t == "S") {
 981            for (let j=0; j<e.v.length;j++) {
 982                const sv = {t: "LC", coefs: {}};
 983                sv.coefs[e.sIdx+j] = ctx.F.one;
 984                res.v.push(sv);
 985            }
 986        } else {
 987            return ctx.throwError(ast, "Type not allowed in array");
 988        }
 989    }
 990
 991    res.s = [ast.values.length*subSize[0], ...subSize];
 992
 993    return res;
 994}
 995
 996function createRefs(ctx, ast, scope) {
 997    const scopeLabels = [];
 998    const scopes = scope ? [scope] : [{}];
 999    iterateAST(ast, (ast, level) => {
1000        while ((scopeLabels.length>0)&&(!level.startsWith(scopeLabels[scopeLabels.length-1]))) {
1001            scopes.pop();
1002            scopeLabels.pop();
1003        }
1004        if (ast.type == "DECLARE") {
1005            if (ast.declareType == "COMPONENT") {
1006                ast.refId = define(ast.name.name, {t: "C"});
1007            } else if ((ast.declareType == "SIGNALIN")||
1008                       (ast.declareType == "SIGNALOUT")||
1009                       (ast.declareType == "SIGNAL")) {
1010                ast.refId = define(ast.name.name, {t: "S"});
1011            } else if (ast.declareType == "VARIABLE") {
1012                ast.refId = define(ast.name.name, {t: "V", s: null});
1013            } else {
1014                return ctx.throwError(ast, "Invalid declaration: " + ast.declareType);
1015            }
1016        } else if (["BLOCK", "FOR", "IF", "WHILE", "COMPUTE"].indexOf(ast.type) >= 0) {
1017            scopeLabels.push(level);
1018            scopes.push({});
1019        } else if (ast.type == "TEMPLATEDEF") {
1020            ast.refId = define(ast.name, {t: "T"});
1021        } else if (ast.type == "FUNCTIONDEF") {
1022            ast.refId = define(ast.name, {t: "F"});
1023        } else if (ast.type == "INCLUDE") {
1024            const incFileName = path.resolve(ctx.filePath, ast.file);
1025            const incFilePath = path.dirname(incFileName);
1026
1027            ctx.includedFiles = ctx.includedFiles || [];
1028            if (ctx.includedFiles[incFileName]) {
1029                ctx.bloc = null; // Include already included...
1030                return;
1031            }
1032
1033            const src = fs.readFileSync(incFileName, "utf8");
1034
1035            if (!src) return ctx.throwError(ast, "Include file not found: "+incFileName);
1036
1037            ctx.includedFiles[incFileName] = src.split("\n");
1038
1039            ast.block = parser.parse(src);
1040            ast.filePath = incFilePath;
1041            ast.fileName = incFileName;
1042
1043            const oldFilePath = ctx.filePath;
1044            const oldFileName = ctx.fileName;
1045            ctx.filePath = incFilePath;
1046            ctx.fileName = incFileName;
1047
1048            scopes.push({});
1049            createRefs(ctx, ast.block);
1050            scopes.pop();
1051
1052            ctx.filePath = oldFilePath;
1053            ctx.fileName = oldFileName;
1054        } else if (ast.type == "VARIABLE") {
1055            ast.refId = name2ref(ast.name);
1056        }
1057
1058        function define(name, entry) {
1059            if (scopes[scopes.length-1][name]) return ctx.throwError(ast, `Name already defined: ${name}`);
1060            for (let i=scopes.length-2; i>=0; i--) {
1061                if (scopes[i][name]) ctx.logWarning(ast, `Shadowing variable: ${name}`);
1062            }
1063            const refId = ctx.refs.length;
1064            entry.refId = refId;
1065            ctx.refs.push(entry);
1066            scopes[scopes.length-1][name] = refId;
1067            return refId;
1068        }
1069
1070        function name2ref(n) {
1071            for (let i=scopes.length-1; i>=0; i--) {
1072                if (typeof scopes[i][n] !== "undefined") return scopes[i][n];
1073            }
1074            return -1;
1075        }
1076
1077
1078    });
1079
1080}
1081
1082