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