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