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