UNPKG

@informalsystems/quint

Version:

Core tool for the Quint specification language

128 lines 6.96 kB
"use strict"; /* ---------------------------------------------------------------------------------- * Copyright 2023 Informal Systems * Licensed under the Apache License, Version 2.0. * See LICENSE in the project root for license information. * --------------------------------------------------------------------------------- */ Object.defineProperty(exports, "__esModule", { value: true }); exports.flattenInstances = void 0; /** * Flattening for instances, creating a new module for each instance and updating names. * * @author Gabriela Moreira * * @module */ const lodash_1 = require("lodash"); const IRTransformer_1 = require("../ir/IRTransformer"); const namespacer_1 = require("../ir/namespacer"); const quintIr_1 = require("../ir/quintIr"); const base_1 = require("../names/base"); const flattener_1 = require("./flattener"); const idRefresher_1 = require("../ir/idRefresher"); /** * Flattens instances from a module, creating a new module for each instance, updating names, and replacing the instance * declaration with an import. Assumes that the module doesn't have any imports and exports (that is, those were already * flattened). * * @param quintModule The module with instances to flatten * @param modulesByName The referenced modules, by name * @param lookupTable The lookup table for the module and its dependencies * @param idGenerator The id generator to be used for generating fresh ids for the new modules * @param sourceMap The source map for the module and its dependencies * @param analysisOutput The analysis output for the module and its dependencies * * @returns The new modules, including the given module (with modifications) and the new modules for the instances */ function flattenInstances(quintModule, modulesByName, lookupTable, idGenerator, sourceMap, analysisOutput) { const flattener = new InstanceFlattener(modulesByName, lookupTable, idGenerator, sourceMap, analysisOutput); const moduleCopy = { ...quintModule, declarations: [...quintModule.declarations] }; const newModule = (0, IRTransformer_1.transformModule)(flattener, moduleCopy); return [...flattener.newModules, newModule]; } exports.flattenInstances = flattenInstances; class InstanceFlattener { constructor(modulesByName, lookupTable, idGenerator, sourceMap, analysisOutput) { // The new modules created by the flattener for each instance. this.newModules = []; this.modulesByName = modulesByName; this.lookupTable = lookupTable; this.idGenerator = idGenerator; this.sourceMap = sourceMap; this.analysisOutput = analysisOutput; } enterModule(quintModule) { this.currentModuleName = quintModule.name; return quintModule; } enterName(expr) { const def = this.lookupTable.get(expr.id); if (def?.importedFrom?.kind !== 'instance') { // Don't change anything from names not related to instances return expr; } const namespace = (0, flattener_1.getNamespaceForDef)(def); return { ...expr, name: (0, lodash_1.compact)([namespace, def.name]).join('::') }; } enterApp(expr) { const def = this.lookupTable.get(expr.id); if (def?.importedFrom?.kind !== 'instance') { // Don't change anything from names not related to instances return expr; } const namespace = (0, flattener_1.getNamespaceForDef)(def); return { ...expr, opcode: (0, lodash_1.compact)([namespace, def.name]).join('::') }; } exitDecl(decl) { if (decl.kind !== 'instance') { // We just want to transform instances. However, it is not possible to use `exitInstance` since that requires us // to return an instance, and in this case we want to return an import. So we use `exitDecl` and ignore // non-instances. return decl; } const protoModule = this.modulesByName.get(decl.protoName); // We are going to construct a new module. const newModuleDecls = []; // A unique name for the new module. To ensure uniqueness, we add the source of the instance (the current module) to // the name, as well as the qualifier (or the proto name itself, if no qualifier is present) // If the current module is named "myModule": // - The module for `import A.*` will be named `myModule::A` // - The module for `import A as B.*` will be named `myModule::B` const newName = [this.currentModuleName, decl.qualifiedName ?? protoModule.name].join('::'); decl.overrides.forEach(([param, expr]) => { // Find dependencies of `expr` and add those to the new module newModuleDecls.push(...(0, flattener_1.dependentDefinitions)(expr, this.lookupTable)); // Add a definition for the parameter, which replaces the constant in the proto module. newModuleDecls.push({ kind: 'def', qualifier: 'pureval', expr: expr, id: param.id, name: [newName, param.name].join('::'), typeAnnotation: this.lookupTable.get(param.id)?.typeAnnotation, }); }); // We will copy everything but the constants, since we already introduce definitions to replace the constants const protoDeclarations = protoModule.declarations.filter(d => d.kind !== 'const'); const transformedDecls = protoDeclarations.map(decl => { if (!(0, quintIr_1.isDef)(decl)) { // Keep imports/exports/instances as is // TODO: further test and explore scenarios where the proto module has imports/exports/instances return decl; } // `flow` is used to compose functions, so that we can apply multiple transformations to the definition. return (0, lodash_1.flow)( // Add the new module's name as the namespace, to make sure the defs are uniquely identified and won't conflict // with other defs in the module (i.e. from other instance). Those will be imported without a qualifier. d => (0, namespacer_1.addNamespaceToDefinition)(d, newName, new Set(base_1.builtinNames)), // Generate new ids for the definition and its subcomponents, since the same definition in different instances // might evaluate to different values. d => (0, idRefresher_1.generateFreshIds)(d, this.idGenerator, this.sourceMap, this.analysisOutput))(decl); }); newModuleDecls.push(...transformedDecls); this.newModules.push({ ...protoModule, declarations: newModuleDecls, name: newName }); // Return the import statement for the new module, which replaces the instance declaration. return { kind: 'import', id: decl.id, protoName: newName, defName: '*' }; } } //# sourceMappingURL=instanceFlattener.js.map