time-to-botec

Benchmark sampling in different programming languages
Log | Files | Refs | README

index.js (10196B)


      1 import { isBindingStatement } from "../../ast/utils.js";
      2 import { defaultEnv } from "../../dist/env.js";
      3 import * as Library from "../../library/index.js";
      4 import { createContext } from "../../reducer/context.js";
      5 import { ImmutableMap } from "../../utility/immutableMap.js";
      6 import * as Result from "../../utility/result.js";
      7 import { vDict } from "../../value/index.js";
      8 import { SqOtherError } from "../SqError.js";
      9 import { SqDict } from "../SqValue/SqDict.js";
     10 import { wrapValue } from "../SqValue/index.js";
     11 import { SqValueContext } from "../SqValueContext.js";
     12 import { SqValuePath } from "../SqValuePath.js";
     13 import { ProjectItem } from "./ProjectItem.js";
     14 function getNeedToRunError() {
     15     return new SqOtherError("Need to run");
     16 }
     17 export class SqProject {
     18     constructor(options) {
     19         this.inverseGraph = new Map();
     20         this.items = new Map();
     21         this.stdLib = options?.stdLib ?? Library.getStdLib();
     22         this.environment = options?.environment ?? defaultEnv;
     23         this.linker = options?.linker;
     24     }
     25     static create(options) {
     26         return new SqProject(options);
     27     }
     28     getEnvironment() {
     29         return this.environment;
     30     }
     31     setEnvironment(environment) {
     32         this.environment = environment;
     33     }
     34     getStdLib() {
     35         return this.stdLib;
     36     }
     37     getSourceIds() {
     38         return Array.from(this.items.keys());
     39     }
     40     getItem(sourceId) {
     41         const item = this.items.get(sourceId);
     42         if (!item) {
     43             throw new Error(`Source ${sourceId} not found`);
     44         }
     45         return item;
     46     }
     47     cleanDependents(initialSourceId) {
     48         const visited = new Set();
     49         const inner = (currentSourceId) => {
     50             visited.add(currentSourceId);
     51             if (currentSourceId !== initialSourceId) {
     52                 this.clean(currentSourceId);
     53             }
     54             for (const sourceId of this.getDependents(currentSourceId)) {
     55                 if (visited.has(sourceId)) {
     56                     continue;
     57                 }
     58                 inner(sourceId);
     59             }
     60         };
     61         inner(initialSourceId);
     62     }
     63     getDependents(sourceId) {
     64         return [...(this.inverseGraph.get(sourceId)?.values() ?? [])];
     65     }
     66     getDependencies(sourceId) {
     67         this.parseImports(sourceId);
     68         return this.getItem(sourceId).getDependencies();
     69     }
     70     removeImportEdges(fromSourceId) {
     71         const item = this.getItem(fromSourceId);
     72         if (item.imports?.ok) {
     73             for (const importData of item.imports.value) {
     74                 this.inverseGraph.get(importData.sourceId)?.delete(fromSourceId);
     75             }
     76         }
     77     }
     78     touchSource(sourceId) {
     79         this.removeImportEdges(sourceId);
     80         this.getItem(sourceId).touchSource();
     81         this.cleanDependents(sourceId);
     82     }
     83     setSource(sourceId, value) {
     84         if (this.items.has(sourceId)) {
     85             this.removeImportEdges(sourceId);
     86             this.getItem(sourceId).setSource(value);
     87             this.cleanDependents(sourceId);
     88         }
     89         else {
     90             this.items.set(sourceId, new ProjectItem({ sourceId, source: value }));
     91         }
     92     }
     93     removeSource(sourceId) {
     94         if (!this.items.has(sourceId)) {
     95             return;
     96         }
     97         this.cleanDependents(sourceId);
     98         this.removeImportEdges(sourceId);
     99         this.setContinues(sourceId, []);
    100         this.items.delete(sourceId);
    101     }
    102     getSource(sourceId) {
    103         return this.items.get(sourceId)?.source;
    104     }
    105     clean(sourceId) {
    106         this.getItem(sourceId).clean();
    107     }
    108     cleanAll() {
    109         this.getSourceIds().forEach((id) => this.clean(id));
    110     }
    111     getImportIds(sourceId) {
    112         const imports = this.getImports(sourceId);
    113         if (!imports) {
    114             return Result.Err(getNeedToRunError());
    115         }
    116         return Result.fmap(imports, (imports) => imports.map((i) => i.sourceId));
    117     }
    118     getImports(sourceId) {
    119         return this.getItem(sourceId).imports;
    120     }
    121     getContinues(sourceId) {
    122         return this.getItem(sourceId).continues;
    123     }
    124     setContinues(sourceId, continues) {
    125         for (const continueId of this.getContinues(sourceId)) {
    126             this.inverseGraph.get(continueId)?.delete(sourceId);
    127         }
    128         for (const continueId of continues) {
    129             if (!this.inverseGraph.has(continueId)) {
    130                 this.inverseGraph.set(continueId, new Set());
    131             }
    132             this.inverseGraph.get(continueId)?.add(sourceId);
    133         }
    134         this.getItem(sourceId).setContinues(continues);
    135         this.cleanDependents(sourceId);
    136     }
    137     getInternalOutput(sourceId) {
    138         return this.getItem(sourceId).output ?? Result.Err(getNeedToRunError());
    139     }
    140     parseImports(sourceId) {
    141         const item = this.getItem(sourceId);
    142         if (item.imports) {
    143             return;
    144         }
    145         item.parseImports(this.linker);
    146         for (const dependencyId of item.getDependencies()) {
    147             if (!this.inverseGraph.has(dependencyId)) {
    148                 this.inverseGraph.set(dependencyId, new Set());
    149             }
    150             this.inverseGraph.get(dependencyId)?.add(sourceId);
    151         }
    152     }
    153     getOutput(sourceId) {
    154         const internalOutputR = this.getInternalOutput(sourceId);
    155         if (!internalOutputR.ok) {
    156             return internalOutputR;
    157         }
    158         const source = this.getSource(sourceId);
    159         if (source === undefined) {
    160             throw new Error("Internal error: source not found");
    161         }
    162         const astR = this.getItem(sourceId).ast;
    163         if (!astR) {
    164             throw new Error("Internal error: AST is missing when result is ok");
    165         }
    166         if (!astR.ok) {
    167             return astR;
    168         }
    169         const ast = astR.value;
    170         const lastStatement = ast.statements.at(-1);
    171         const hasEndExpression = !!lastStatement && !isBindingStatement(lastStatement);
    172         const result = wrapValue(internalOutputR.value.result, new SqValueContext({
    173             project: this,
    174             sourceId,
    175             source,
    176             ast,
    177             valueAst: hasEndExpression ? lastStatement : ast,
    178             valueAstIsPrecise: hasEndExpression,
    179             path: new SqValuePath({
    180                 root: "result",
    181                 items: [],
    182             }),
    183         }));
    184         const [bindings, exports] = ["bindings", "exports"].map((field) => new SqDict(internalOutputR.value[field], new SqValueContext({
    185             project: this,
    186             sourceId,
    187             source,
    188             ast: astR.value,
    189             valueAst: astR.value,
    190             valueAstIsPrecise: true,
    191             path: new SqValuePath({
    192                 root: "bindings",
    193                 items: [],
    194             }),
    195         })));
    196         return Result.Ok({ result, bindings, exports });
    197     }
    198     getResult(sourceId) {
    199         return Result.fmap(this.getOutput(sourceId), ({ result }) => result);
    200     }
    201     getBindings(sourceId) {
    202         return Result.fmap(this.getOutput(sourceId), ({ bindings }) => bindings);
    203     }
    204     async buildExternals(sourceId, pendingIds) {
    205         this.parseImports(sourceId);
    206         const rImports = this.getImports(sourceId);
    207         if (!rImports) {
    208             throw new Error("Internal logic error");
    209         }
    210         if (!rImports.ok) {
    211             return rImports;
    212         }
    213         let externals = ImmutableMap().merge(this.getStdLib());
    214         for (const importBinding of [
    215             ...this.getItem(sourceId).getImplicitImports(),
    216             ...rImports.value,
    217         ]) {
    218             if (!this.items.has(importBinding.sourceId)) {
    219                 if (!this.linker) {
    220                     throw new Error(`Can't load source for ${importBinding.sourceId}, linker is missing`);
    221                 }
    222                 let newSource;
    223                 try {
    224                     newSource = await this.linker.loadSource(importBinding.sourceId);
    225                 }
    226                 catch (e) {
    227                     return Result.Err(new SqOtherError(`Failed to load import ${importBinding.sourceId}`));
    228                 }
    229                 this.setSource(importBinding.sourceId, newSource);
    230             }
    231             if (pendingIds.has(importBinding.sourceId)) {
    232                 return Result.Err(new SqOtherError(`Cyclic import ${importBinding.sourceId}`));
    233             }
    234             await this.innerRun(importBinding.sourceId, pendingIds);
    235             const outputR = this.getInternalOutput(importBinding.sourceId);
    236             if (!outputR.ok) {
    237                 return outputR;
    238             }
    239             switch (importBinding.type) {
    240                 case "flat":
    241                     externals = externals.merge(outputR.value.bindings);
    242                     break;
    243                 case "named":
    244                     externals = externals.set(importBinding.variable, vDict(outputR.value.exports));
    245                     break;
    246                 default:
    247                     throw new Error(`Internal error, ${importBinding}`);
    248             }
    249         }
    250         return Result.Ok(externals);
    251     }
    252     async innerRun(sourceId, pendingIds) {
    253         pendingIds.add(sourceId);
    254         const cachedOutput = this.getItem(sourceId).output;
    255         if (!cachedOutput) {
    256             const rExternals = await this.buildExternals(sourceId, pendingIds);
    257             if (!rExternals.ok) {
    258                 this.getItem(sourceId).failRun(rExternals.value);
    259             }
    260             else {
    261                 const context = createContext(this.getEnvironment());
    262                 await this.getItem(sourceId).run(context, rExternals.value);
    263             }
    264         }
    265         pendingIds.delete(sourceId);
    266     }
    267     async run(sourceId) {
    268         await this.innerRun(sourceId, new Set());
    269     }
    270     findValuePathByOffset(sourceId, offset) {
    271         const { ast } = this.getItem(sourceId);
    272         if (!ast) {
    273             return Result.Err(new SqOtherError("Not parsed"));
    274         }
    275         if (!ast.ok) {
    276             return ast;
    277         }
    278         const found = SqValuePath.findByOffset({
    279             ast: ast.value,
    280             offset,
    281         });
    282         if (!found) {
    283             return Result.Err(new SqOtherError("Not found"));
    284         }
    285         return Result.Ok(found);
    286     }
    287 }
    288 //# sourceMappingURL=index.js.map