UNPKG

@informalsystems/quint

Version:

Core tool for the Quint specification language

134 lines 5.51 kB
"use strict"; /* ---------------------------------------------------------------------------------- * Copyright 2022 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.applySubstitutionToEntity = exports.applySubstitution = exports.compose = void 0; /** * Substitutions for effects and its entities, including composition and application * * @author Gabriela Moreira * * @module */ const either_1 = require("@sweet-monads/either"); const errorTree_1 = require("../errorTree"); const printing_1 = require("./printing"); const simplification_1 = require("./simplification"); const lodash_1 = require("lodash"); /* * Compose two substitutions by applying the first one to the second one's values * * @param s1 substitutions to be applied and returned unchanged * @param s2 substitutions to be updated and returned * * @returns a new substitutions list containing the composition of given substitutions, if succeeded. * Otherwise, an error tree with the substitution application failure */ function compose(s1, s2) { return applySubstitutionsToSubstitutions(s1, s2) .map((sb) => sb.concat(s1)) .mapLeft(error => (0, errorTree_1.buildErrorTree)(`Composing substitutions ${(0, printing_1.substitutionsToString)(s1)} and ${(0, printing_1.substitutionsToString)(s2)}`, error)); } exports.compose = compose; /** * Applies substitutions to an effect, replacing all variable names with their * substitution values when they are defined. * * @param subs the substitutions to be applied * @param e the effect to be transformed * * @returns the effect resulting from the substitutions' application on the given * effect, when successful. Otherwise, an error tree with an error message and its trace. */ function applySubstitution(subs, e) { let result = (0, either_1.right)(e); switch (e.kind) { case 'variable': { // e is an effect entity const sub = subs.find(s => s.name === e.name); if (sub && sub.kind === 'effect') { result = (0, either_1.right)(sub.value); } break; } case 'arrow': { // e takes effects as parameters and returns an effect as result const arrowParams = (0, either_1.mergeInMany)(e.params.map(ef => applySubstitution(subs, ef))); result = arrowParams .chain(ps => { const arrowResult = applySubstitution(subs, e.result); return arrowResult.map(r => ({ kind: e.kind, params: ps, result: r })); }) .mapLeft(error => (0, errorTree_1.buildErrorTree)(`Applying substitution to arrow effect ${(0, printing_1.effectToString)(e)}`, error)); break; } case 'concrete': { // e is an effect of the form Read[r] & Update[u] or Read[r] & Temporal[t] const components = e.components .map(c => ({ kind: c.kind, entity: applySubstitutionToEntity(subs, c.entity) })) .filter(c => !emptyEntity(c.entity)); result = (0, either_1.right)({ kind: 'concrete', components }); break; } } return result.map(simplification_1.simplify).chain(r => { if (!(0, lodash_1.isEqual)(r, e)) { // Keep re-applying the substitutions until the effect is unchanged. // Useful when substitutions have a transitive pattern [ a |-> b, b |-> c ] return applySubstitution(subs, r); } return (0, either_1.right)(r); }); } exports.applySubstitution = applySubstitution; /** * Applies substitutions to an entity, replacing all variable names with their * substitution values when they are defined. * * @param subs the substitutions to be applied * @param entity the entity to be transformed * * @returns the entity resulting from the substitutions' application on the * given entity */ function applySubstitutionToEntity(subs, entity) { switch (entity.kind) { case 'variable': { const sub = subs.find(s => s.name === entity.name); if (sub && sub.kind === 'entity') { return sub.value; } break; } case 'union': { const newEntities = entity.entities.map(v => applySubstitutionToEntity(subs, v)); return { kind: 'union', entities: newEntities }; } } return entity; } exports.applySubstitutionToEntity = applySubstitutionToEntity; function emptyEntity(entity) { switch (entity.kind) { case 'concrete': return entity.stateVariables.length === 0; case 'variable': return false; case 'union': return entity.entities.length === 0; } } function applySubstitutionsToSubstitutions(s1, s2) { return (0, either_1.mergeInMany)(s2.map((s) => { switch (s.kind) { case 'effect': return applySubstitution(s1, s.value).map(v => [{ kind: s.kind, name: s.name, value: v }]); case 'entity': return (0, either_1.right)([{ kind: s.kind, name: s.name, value: applySubstitutionToEntity(s1, s.value) }]); } })).map(s => s.flat()); } //# sourceMappingURL=substitutions.js.map