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            if (isIn) {
 433                sDest.e = sIdx;
 434            } else if (isOut) {
 435                sSrc.e = dIdx;
 436            } else {
 437                sDest.e = sIdx;
 438            }
 439            if (!isOut) {
 440                if (utils.isDefined(sSrc.v)) sDest.v = sSrc.v;
 441            }
 442        }
 443    }
 444}
 445
 446
 447
 448function execInstantiateComponet(ctx, vr, fn, sels) {
 449
 450    if (fn.type != "FUNCTIONCALL") return ctx.throwError(fn, "Right type of instantiate component must be a function call");
 451
 452    const templateName = fn.name;
 453
 454    const template = ctx.templates[templateName];
 455    if (!template) return ctx.throwError(fn, "Invalid Template");
 456
 457    const paramValues = [];
 458    for (let i=0; i< fn.params.length; i++) {
 459        const v = exec(ctx, fn.params[i]);
 460        if (ctx.error) return;
 461        for (let j=0; j<v.s[0]; j++) {
 462            if (v.v[j].t != "N") ctx.throwError(fn, "Parameters of a template must be constant");
 463        }
 464        paramValues.push(v);
 465    }
 466    if (template.params.length != paramValues.length) ctx.throwError(fn, "Invalid Number of parameters");
 467
 468    let o=0;
 469    for (let i=0; i<sels.length; i++) o += sels[i] * vr.s[i+1];
 470    for (let i=o; i<o+vr.s[sels.length]; i++) {
 471        instantiateComponent(vr.cIdx+i);
 472    }
 473
 474    function instantiateComponent(cIdx) {
 475
 476        function extractValue(sizes, arr) {
 477            if (sizes[0] == 1) return arr[0].v.toString();
 478            const res = [];
 479            for (let i=0; i<sizes[0]; i += sizes[1]) {
 480                res.push(extractValue(sizes.slice(1), arr.slice(i, i+sizes[1])));
 481            }
 482            return res;
 483        }
 484
 485        if (ctx.components[cIdx]) return ctx.throwError(fn, "Component already instantiated");
 486
 487        const oldComponent = ctx.currentComponent;
 488        const oldFileName = ctx.fileName;
 489        const oldFilePath = ctx.filePath;
 490        const oldMain = ctx.main;
 491        const oldRefs = ctx.refs;
 492
 493        if (ctx.currentComponent==-1) {
 494            ctx.main=true;
 495        } else {
 496            ctx.main=false;
 497        }
 498
 499        ctx.currentComponent = cIdx;
 500
 501        ctx.components[cIdx] = {
 502            params: {},
 503            names: ctx.newTableName(),
 504            nInSignals: 0,
 505            template: templateName
 506        };
 507
 508
 509        if (template.params.length != paramValues.length) return ctx.throwError(fn, "Invalid number of parameters: " + templateName);
 510        ctx.refs = [];
 511
 512        const scope = {};
 513        for (let i=0; i< template.params.length; i++) {
 514            ctx.refs.push({
 515                t: "V",
 516                s: paramValues[i].s,
 517                v: paramValues[i].v,
 518                refId: i
 519            });
 520            scope[template.params[i]] = i;
 521            ctx.components[cIdx].params[template.params[i]] = extractValue(paramValues[i].s, paramValues[i].v);
 522        }
 523
 524        createRefs(ctx, template.block, scope);
 525
 526        ctx.fileName = template.fileName;
 527        ctx.filePath = template.filePath;
 528
 529        execBlock(ctx, template.block);
 530
 531        ctx.refs = oldRefs;
 532        ctx.fileName = oldFileName;
 533        ctx.filePath = oldFilePath;
 534        ctx.currentComponent = oldComponent;
 535        ctx.main = oldMain;
 536    }
 537}
 538
 539
 540function execBlock(ctx,  ast) {
 541    for (let i=0; i<ast.statements.length; i++) {
 542        exec(ctx, ast.statements[i]);
 543        if (ctx.returnValue) return;
 544        if (ctx.error) return;
 545    }
 546}
 547
 548function clone(a) {
 549    const res = {
 550        t: a.t,
 551        s: a.s
 552    };
 553    if (a.t == "V") {
 554        res.v = new Array(a.v.length);
 555        for (let i=0; i<a.v.length; i++) res.v[i] = a.v[i];
 556    } else if (a.t == "S") {
 557        res.sIdx = a.sIdx;
 558    } else if (a.t == "C") {
 559        res.cIdx = a.cIdx;
 560    }
 561    return res;
 562}
 563
 564function execFunctionCall(ctx, ast) {
 565
 566    if (ast.name == "log") {
 567        const v = exec(ctx, ast.params[0]);
 568        const ev = val(ctx, v, ast);
 569        if (ev.v) {
 570            console.log(ev.v.toString());
 571        } else {
 572            console.log(JSON.stringify(stringifyBigInts(ev)));
 573        }
 574        return;
 575    }
 576    if (ast.name == "assert") {
 577        ast.fileName = ctx.fileName;
 578        ast.filePath = ctx.filePath;
 579
 580        const v = exec(ctx, ast.params[0]);
 581        const ev = val(ctx, v, ast);
 582        if ((typeof ev.v !== "undefined")&&(ctx.F.isZero(ev.v))) return ctx.throwError(ast, "Assertion failed");
 583        return;
 584    }
 585
 586    const fnc = ctx.functions[ast.name];
 587
 588    if (!fnc) return ctx.throwError(ast, "Function not defined");
 589
 590    const paramValues = [];
 591    for (let i=0; i< ast.params.length; i++) {
 592        const v = exec(ctx, ast.params[i]);
 593        if (ctx.error) return;
 594
 595        paramValues.push(v);
 596    }
 597
 598    if (ast.params.length != paramValues.length) ctx.throwError(ast, "Invalid Number of parameters");
 599
 600    const oldFileName = ctx.fileName;
 601    const oldFilePath = ctx.filePath;
 602    const oldRefs = ctx.refs;
 603
 604    ctx.fileName = fnc.fileName;
 605    ctx.filePath = fnc.filePath;
 606    ctx.refs = [];
 607
 608    const scope = {};
 609    for (let i=0; i< fnc.params.length; i++) {
 610        const entry = clone(paramValues[i]);
 611        entry.refId = i;
 612        scope[fnc.params[i]] = i;
 613        ctx.refs.push(entry);
 614    }
 615
 616    createRefs(ctx, fnc.block, scope);
 617
 618    execBlock(ctx, fnc.block);
 619
 620    const res = ctx.returnValue;
 621    ctx.returnValue = null;
 622
 623    ctx.fileName = oldFileName;
 624    ctx.filePath = oldFilePath;
 625    ctx.refs = oldRefs;
 626
 627    return res;
 628}
 629
 630function execReturn(ctx, ast) {
 631    ctx.returnValue = exec(ctx, ast.value);
 632    return;
 633}
 634
 635
 636function execVariable(ctx, ast) {
 637
 638    const v = ctx.refs[ast.refId];
 639    if (!v) {
 640        return ctx.throwError(ast, "Variable not defined: "+ast.name);
 641    }
 642
 643    const sels = [];
 644    for (let i=0; i< ast.selectors.length; i++) {
 645        const sel = exec(ctx, ast.selectors[i]);
 646        if (ctx.error) return;
 647        if (sel.s[0] != 1) return ctx.throwError(ast, "Variable selector cannot be an array");
 648        if (sel.v[0].t != "N") return NQVAL;
 649        sels.push(Scalar.toNumber(sel.v[0].v));
 650    }
 651
 652    let o = 0;
 653    let s = v.s[0];
 654
 655    if (sels.length > v.s.length-2) return ctx.throwError(ast, "Too many selectors");
 656    for (let i=0; i<sels.length; i++) {
 657        if (sels[i] >= v.s[i] / v.s[i+1]) return ctx.throwError(ast, "Out of Range");
 658        if (sels[i] < 0 ) return ctx.throwError(ast, "selector negative");
 659        o += sels[i] * v.s[i+1];
 660        s = v.s[i+1];
 661    }
 662
 663    if (v.t == "V") {
 664        return {
 665            t: "V",
 666            s: v.s.slice(sels.length),
 667            v: v.v.slice(o, o+s)
 668        };
 669    } else if (v.t == "S") {
 670        return {
 671            t: "S",
 672            s: v.s.slice(sels.length),
 673            sIdx: o + v.sIdx
 674        };
 675    } else if (v.t == "C") {
 676        return {
 677            t: "C",
 678            s: v.s.slice(sels.length),
 679            cIdx: o
 680        };
 681    } else {
 682        assert(false);
 683    }
 684}
 685
 686
 687
 688
 689function execPin(ctx, ast) {
 690    const selsC = [];
 691    for (let i=0; i< ast.component.selectors.length; i++) {
 692        const sel = exec(ctx, ast.component.selectors[i]);
 693        if (ctx.error) return;
 694
 695        if (sel.s[0] != 1) return ctx.throwError(ast, "Component selector cannot be an array");
 696        if (sel.v[0].t != "N") return NQVAL;
 697        selsC.push(Scalar.toNumber(sel.v[0].v));
 698    }
 699
 700    const cIdx = ctx.getComponentIdx(ast.component.name, selsC);
 701    if (cIdx<0) return ctx.throwError(ast.component, "Component does not exists: "+ast.component.name);
 702
 703    const selsP = [];
 704    for (let i=0; i< ast.pin.selectors.length; i++) {
 705        const sel = exec(ctx, ast.pin.selectors[i]);
 706        if (ctx.error) return;
 707        if (sel.s[0] != 1) return ctx.throwError(ast, "Signal selector cannot be an array");
 708        if (sel.v[0].t != "N") return NQVAL;
 709        selsP.push(Scalar.toNumber(sel.v[0].v));
 710    }
 711    if (!ctx.components[cIdx]) {
 712        return ctx.throwError(ast, "Component not defined yet");
 713    }
 714    const sIdx = ctx.components[cIdx].names.getSignalIdx(ast.pin.name, selsP);
 715
 716    if (sIdx<0) return ctx.throwError(ast, "Signal not defined:" + buildFullName() );
 717    return {
 718        t: "S",
 719        sIdx: sIdx,
 720        s: utils.accSizes(ctx.components[cIdx].names.o[ast.pin.name].sizes).slice(selsP.length)
 721    };
 722
 723    function buildFullName() {
 724        return ast.component.name + sels2str(selsC) + "." + ast.pin.name + sels2str(selsP);
 725    }
 726
 727    function sels2str(sels) {
 728        let S = "";
 729        for (let i=0; i< sels.length; i++) {
 730            S += "[" + sels[i] + "]";
 731        }
 732        return S;
 733    }
 734}
 735
 736function execLoop(ctx, ast) {
 737
 738    if (ast.init) {
 739        exec(ctx, ast.init);
 740        if (ctx.error) return;
 741    }
 742
 743    let v = exec(ctx, ast.condition);
 744    if (ctx.error) return;
 745    if (v.s[0] != 1) return ctx.throwError(ast.condition, "Condition in loop cannot be an array");
 746    if (v.v[0].t != "N") {
 747        iterateAST(ast, (ast) => {
 748            if (ast.type == "OP") {
 749                if (["==>", "<==", "==="].indexOf(ast.op) >= 0) {
 750                    return ctx.throwError(ast.condition, "Constraint inside a calculating block");
 751                }
 752            }
 753        });
 754
 755        // Assert no constraints
 756        return;
 757    }
 758
 759    while ((! ctx.F.isZero(v.v[0].v))&&(!ctx.returnValue)) {
 760        exec(ctx, ast.body);
 761        if (ctx.error) return;
 762
 763        if (ast.step) {
 764            exec(ctx, ast.step);
 765            if (ctx.error) return;
 766        }
 767
 768        v = exec(ctx, ast.condition);
 769        if (ctx.error) return;
 770        if (v.s[0] != 1) return ctx.throwError(ast.condition, "Condition in loop cannot be an array");
 771        if (v.v[0].t != "N") return ctx.throwError(ast.condition, "Condition result not a number");
 772    }
 773}
 774
 775function execCompute(ctx, ast) {
 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
 785function execIf(ctx, ast) {
 786    let v = exec(ctx, ast.condition);
 787    if (ctx.error) return;
 788    if (v.s[0] != 1) return ctx.throwError(ast.condition, "Condition cannot be an array");
 789    if (v.v[0].t != "N") {
 790        iterateAST(ast, (ast) => {
 791            if (ast.type == "OP") {
 792                if (["==>", "<==", "==="].indexOf(ast.op) >= 0) {
 793                    return ctx.throwError(ast.condition, "Constraint inside a calculating block");
 794                }
 795            }
 796        });
 797
 798        // Assert no constraints
 799        return;
 800    }
 801
 802    if (!ctx.F.isZero(v.v[0].v)) {
 803        exec(ctx, ast.then);
 804    } else {
 805        if (ast.else) {
 806            exec(ctx, ast.else);
 807        }
 808    }
 809}
 810
 811
 812function execTerCon(ctx, ast) {
 813    let v = exec(ctx, ast.values[0]);
 814    if (ctx.error) return;
 815    if (v.s[0] != 1) return ctx.throwError(ast.condition, "Condition cannot be an array");
 816    if (v.v[0].t != "N") {
 817        iterateAST(ast, (ast) => {
 818            if (ast.type == "OP") {
 819                if (["==>", "<==", "==="].indexOf(ast.op) >= 0) {
 820                    return ctx.throwError(ast.condition, "Constraint inside a calculating block");
 821                }
 822            }
 823        });
 824
 825        // Assert no constraints
 826        return NQVAL;
 827    }
 828
 829    if (!ctx.F.isZero(v.v[0].v)) {
 830        return exec(ctx, ast.values[1]);
 831    } else {
 832        return exec(ctx, ast.values[2]);
 833    }
 834}
 835
 836function execOp(ctx, ast, op, nOps) {
 837    const operands = [];
 838    for (let i=0; i<nOps; i++) {
 839        const a = exec(ctx, ast.values[i]);
 840        if (ctx.error) return;
 841        const aV = val(ctx, a, ast.values[i]);
 842        operands.push(aV);
 843    }
 844
 845    return  {
 846        t: "V",
 847        s: [1,0],
 848        v: [ctx.lc[op](...operands)]
 849    };
 850}
 851
 852
 853function execOpOp(ctx, ast, op, lr) {
 854
 855    if (ast.values[0].type != "VARIABLE") return ctx.throwError(ast, "incrementing a non variable");
 856    if (!utils.isDefined(ast.values[0].refId)) return ctx.throwError(ast, "Assigning to a non variable");
 857    const left = ctx.refs[ast.values[0].refId];
 858    if (left.t != "V") return ctx.throwError(ast, `Only variables can use ${ast.op} operations`);
 859    let leftSels = [];
 860    if (ast.values[0].selectors) {
 861        for (let i=0; i< ast.values[0].selectors.length; i++) {
 862            const sel = exec(ctx, ast.values[0].selectors[i]);
 863            if (ctx.error) return;
 864
 865            if (sel.s[0] != 1) return ctx.throwError(ast, "Selector cannot be an array");
 866            if (sel.v[0].t != "N") return {t: "NQ"};
 867
 868            leftSels.push( Scalar.toNumber(sel.v[0].v) );
 869        }
 870    }
 871    if (!left.s) return ctx.throwError(ast, "variable. not defined yet");
 872
 873    let o = 0;
 874    let s = left.s[0];
 875    for (let i=0; i<leftSels.length; i++) {
 876        o += leftSels[i]*left.s[i+1];
 877        s = left.s[i+1];
 878    }
 879    if (s != 1) return ctx.throwError(ast, `invalid operator ${ast.op} in an array`);
 880    const resBefore = left.v[o];
 881
 882    let right;
 883    if (ast.values[1]) {
 884        const rightRef = exec(ctx, ast.values[1]);
 885        if (ctx.error) return;
 886        right = val(ctx, rightRef);
 887    } else {
 888        right = {t:"N", v: ctx.F.one};
 889    }
 890
 891    if (!right) return ctx.throwError(ast, "right operand is not initialized");
 892    if (!resBefore) return ctx.throwError(ast, "left operand is not initialized");
 893
 894    const resAfter = ctx.lc[op](resBefore, right);
 895    left.v[o] = resAfter;
 896
 897    if (lr == "RIGHT") {
 898        return {
 899            t: "V",
 900            s: [1,0],
 901            v: [resBefore]
 902        };
 903    } else {
 904        return {
 905            t: "V",
 906            s: [1,0],
 907            v: [resAfter]
 908        };
 909    }
 910}
 911
 912// Extract the value of a reference.
 913// Must be a single value. (cannot be an array)
 914// The result is an LCAlgebra compatible object {t: ["N"|"LC"|"QEX"|"NQ"], [coefs|v|a,b,c]}
 915function val(ctx, a, ast) {
 916    if (a.s[0] != 1) return ctx.throwError(ast, "Array not allowed");
 917    if (a.t=="V") {
 918        return a.v[0];
 919    } else if (a.t=="S") {
 920        const res = {
 921            t: "LC",
 922            coefs: {}
 923        };
 924        let sIdx = a.sIdx;
 925        while (ctx.signals[sIdx].e >= 0) sIdx = ctx.signals[sIdx].e;
 926        res.coefs[sIdx] = ctx.F.one;
 927        return res;
 928    } else {
 929        ctx.throwError(ast, "Invalid type: " + a.t);
 930    }
 931}
 932
 933function execConstrain(ctx, ast) {
 934    ast.fileName = ctx.fileName;
 935    ast.filePath = ctx.filePath;
 936    const a = exec(ctx, ast.values[0]);
 937    if (ctx.error) return;
 938    const aV = val(ctx, a, ast.values[0]);
 939    const b = exec(ctx, ast.values[1]);
 940    if (ctx.error) return;
 941    const bV = val(ctx, b, ast.values[1]);
 942
 943    const res = ctx.lc.sub(aV,bV);
 944    if (res.type == "NQ") return ctx.throwError(ast, "Non Quadratic constraint");
 945
 946    if (!ctx.lc.isZero(res)) {
 947        ctx.constraints.push(ctx.lc.toQEX(res));
 948        if (ctx.verbose) {
 949            if ((ctx.constraints.length % 10000 == 0)&&(ctx.constraints.length>0)) console.log("Constraints: " + ctx.constraints.length);
 950        }
 951    }
 952
 953    return a;
 954}
 955
 956function execSignalAssignConstrain(ctx, ast) {
 957    const v = execAssignement(ctx,ast);
 958    if (ctx.error) return;
 959    execConstrain(ctx, ast);
 960    if (ctx.error) return;
 961    return v;
 962}
 963
 964function execInclude(ctx, ast) {
 965    if (ast.block) {
 966
 967        const oldFilePath = ctx.filePath;
 968        const oldFileName = ctx.fileName;
 969
 970        ctx.filePath = ast.filePath;
 971        ctx.fileName = ast.fileName;
 972
 973        exec(ctx, ast.block);  // Process only one.
 974
 975        ctx.filePath = oldFilePath;
 976        ctx.fileName = oldFileName;
 977    }
 978}
 979
 980function execArray(ctx, ast) {
 981    const res = {t: "V", v:[]};
 982
 983
 984    let subSize = null;
 985
 986    for (let i=0; i<ast.values.length; i++) {
 987        const e = exec(ctx, ast.values[i]);
 988        if (i==0) {
 989            subSize=e.s;
 990        } else {
 991            if (!utils.sameSizes(subSize, e.s)) return ctx.throwError(ast, "Array mus be homogeneous");
 992        }
 993        if (e.t == "V") {
 994            for (let j=0; j<e.v.length;j++) res.v.push(e.v[j]);
 995        } else if (e.t == "S") {
 996            for (let j=0; j<e.v.length;j++) {
 997                const sv = {t: "LC", coefs: {}};
 998                sv.coefs[e.sIdx+j] = ctx.F.one;
 999                res.v.push(sv);
1000            }
1001        } else {
1002            return ctx.throwError(ast, "Type not allowed in array");
1003        }
1004    }
1005
1006    res.s = [ast.values.length*subSize[0], ...subSize];
1007
1008    return res;
1009}
1010
1011function createRefs(ctx, ast, scope) {
1012    const scopeLabels = [];
1013    const scopes = scope ? [scope] : [{}];
1014    iterateAST(ast, (ast, level) => {
1015        while ((scopeLabels.length>0)&&(!level.startsWith(scopeLabels[scopeLabels.length-1]))) {
1016            scopes.pop();
1017            scopeLabels.pop();
1018        }
1019        if (ast.type == "DECLARE") {
1020            if (ast.declareType == "COMPONENT") {
1021                ast.refId = define(ast.name.name, {t: "C"});
1022            } else if ((ast.declareType == "SIGNALIN")||
1023                       (ast.declareType == "SIGNALOUT")||
1024                       (ast.declareType == "SIGNAL")) {
1025                ast.refId = define(ast.name.name, {t: "S"});
1026            } else if (ast.declareType == "VARIABLE") {
1027                ast.refId = define(ast.name.name, {t: "V", s: null});
1028            } else {
1029                return ctx.throwError(ast, "Invalid declaration: " + ast.declareType);
1030            }
1031        } else if (["BLOCK", "FOR", "IF", "WHILE", "COMPUTE"].indexOf(ast.type) >= 0) {
1032            scopeLabels.push(level);
1033            scopes.push({});
1034        } else if (ast.type == "TEMPLATEDEF") {
1035            ast.refId = define(ast.name, {t: "T"});
1036        } else if (ast.type == "FUNCTIONDEF") {
1037            ast.refId = define(ast.name, {t: "F"});
1038        } else if (ast.type == "INCLUDE") {
1039            const incFileName = path.resolve(ctx.filePath, ast.file);
1040            const incFilePath = path.dirname(incFileName);
1041
1042            ctx.includedFiles = ctx.includedFiles || [];
1043            if (ctx.includedFiles[incFileName]) {
1044                ctx.bloc = null; // Include already included...
1045                return;
1046            }
1047
1048            const src = fs.readFileSync(incFileName, "utf8");
1049
1050            if (!src) return ctx.throwError(ast, "Include file not found: "+incFileName);
1051
1052            ctx.includedFiles[incFileName] = src.split("\n");
1053
1054            ast.block = parser.parse(src);
1055            ast.filePath = incFilePath;
1056            ast.fileName = incFileName;
1057
1058            const oldFilePath = ctx.filePath;
1059            const oldFileName = ctx.fileName;
1060            ctx.filePath = incFilePath;
1061            ctx.fileName = incFileName;
1062
1063            scopes.push({});
1064            createRefs(ctx, ast.block);
1065            scopes.pop();
1066
1067            ctx.filePath = oldFilePath;
1068            ctx.fileName = oldFileName;
1069        } else if (ast.type == "VARIABLE") {
1070            ast.refId = name2ref(ast.name);
1071        }
1072
1073        function define(name, entry) {
1074            if (scopes[scopes.length-1][name]) return ctx.throwError(ast, `Name already defined: ${name}`);
1075            for (let i=scopes.length-2; i>=0; i--) {
1076                if (scopes[i][name]) ctx.logWarning(ast, `Shadowing variable: ${name}`);
1077            }
1078            const refId = ctx.refs.length;
1079            entry.refId = refId;
1080            ctx.refs.push(entry);
1081            scopes[scopes.length-1][name] = refId;
1082            return refId;
1083        }
1084
1085        function name2ref(n) {
1086            for (let i=scopes.length-1; i>=0; i--) {
1087                if (typeof scopes[i][n] !== "undefined") return scopes[i][n];
1088            }
1089            return -1;
1090        }
1091
1092
1093    });
1094
1095}
1096
1097