@informalsystems/quint
Version:
Core tool for the Quint specification language
134 lines • 5.51 kB
JavaScript
;
/* ----------------------------------------------------------------------------------
* 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