@informalsystems/quint
Version:
Core tool for the Quint specification language
185 lines • 7.09 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.applySubstitutionToConstraint = exports.applySubstitution = exports.compose = void 0;
const errorTree_1 = require("../errorTree");
const constraintSolver_1 = require("./constraintSolver");
const printing_1 = require("./printing");
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
*/
function compose(table, s1, s2) {
const newS2 = applySubstitutionsToSubstitutions(table, s1, s2);
return newS2.concat(s1);
}
exports.compose = compose;
/**
* Applies substitutions to a type, replacing all type variables with their
* substitution values when they are defined.
*
* @param subs the substitutions to be applied
* @param t the type to be transformed
*
* @returns the type resulting from the substitutions' application on the
* given type
*/
function applySubstitution(table, subs, t) {
// We cannot assign the result of a switch to a value, so we use an arrow function instead
const singleApplication = () => {
switch (t.kind) {
case 'var': {
const sub = subs.find(s => s.name === t.name);
if (sub && sub.kind === 'type') {
return sub.value;
}
else {
return t;
}
}
case 'oper': {
const arrowParams = t.args.map(ef => applySubstitution(table, subs, ef));
const arrowResult = applySubstitution(table, subs, t.res);
return { kind: t.kind, args: arrowParams, res: arrowResult, id: t.id };
}
case 'list':
case 'set': {
return { kind: t.kind, elem: applySubstitution(table, subs, t.elem), id: t.id };
}
case 'fun': {
return {
kind: t.kind,
arg: applySubstitution(table, subs, t.arg),
res: applySubstitution(table, subs, t.res),
id: t.id,
};
}
case 'tup': {
return { kind: t.kind, fields: applySubstitutionToRow(table, subs, t.fields), id: t.id };
}
case 'rec': {
return {
kind: t.kind,
fields: applySubstitutionToRow(table, subs, t.fields),
id: t.id,
};
}
case 'sum':
return {
...t,
// We know this has to end up as a concrete fixed row, since it must
// start as one, and applying substitutions cannot result in a wider type
fields: applySubstitutionToRow(table, subs, t.fields),
};
case 'app':
return {
...t,
args: t.args.map(a => applySubstitution(table, subs, a)),
};
// The basic types have no variables, so don't require substitution
case 'int':
case 'bool':
case 'str':
case 'const':
return t;
}
};
const result = singleApplication();
if ((0, lodash_1.isEqual)(result, t)) {
return t;
}
else {
return applySubstitution(table, subs, result);
}
}
exports.applySubstitution = applySubstitution;
/**
* Applies substitutions to a constraint, by applying the substitutions to all
* types occurring in that constraint
*
* @param subs the substitutions to be applied
* @param c the constraint to be transformed
*
* @returns the constraint resulting from the substitutions' application on the
* given constraint
*/
function applySubstitutionToConstraint(table, subs, c) {
switch (c.kind) {
case 'empty':
return c;
case 'isDefined':
return { ...c, type: applySubstitution(table, subs, c.type) };
case 'eq': {
const ts = [
applySubstitution(table, subs, c.types[0]),
applySubstitution(table, subs, c.types[1]),
];
return { kind: c.kind, types: ts, sourceId: c.sourceId };
}
case 'conjunction': {
const cs = c.constraints.map(con => applySubstitutionToConstraint(table, subs, con));
return { kind: 'conjunction', constraints: cs, sourceId: c.sourceId };
}
}
}
exports.applySubstitutionToConstraint = applySubstitutionToConstraint;
function applySubstitutionsToSubstitutions(table, s1, s2) {
return s2.flatMap(s => {
const sub = s1.find(sub => s.name === sub.name);
if (sub) {
let result;
if (sub.kind === 'type' && s.kind === 'type') {
result = (0, constraintSolver_1.unify)(table, s.value, sub.value);
}
else if (sub.kind === 'row' && s.kind === 'row') {
result = (0, constraintSolver_1.unifyRows)(table, s.value, sub.value);
}
else {
throw new Error(`Substitutions with same name (${s.name}) but incompatible kinds: ${(0, printing_1.substitutionsToString)([sub, s])}`);
}
if (result.isLeft()) {
throw new Error(`Unifying substitutions with same name: ${s.name}, ${(0, errorTree_1.errorTreeToString)(result.value)}`);
}
else {
return result.value;
}
}
switch (s.kind) {
case 'type':
return [{ kind: s.kind, name: s.name, value: applySubstitution(table, s1, s.value) }];
case 'row':
return [{ kind: s.kind, name: s.name, value: applySubstitutionToRow(table, s1, s.value) }];
}
});
}
function applySubstitutionToRow(table, s, r) {
switch (r.kind) {
case 'row':
return {
kind: 'row',
fields: r.fields.map(f => ({ fieldName: f.fieldName, fieldType: applySubstitution(table, s, f.fieldType) })),
other: applySubstitutionToRow(table, s, r.other),
};
case 'var': {
const sub = s.find(s => s.name === r.name && s.kind === 'row');
if (sub && sub.kind === 'row') {
return sub.value;
}
else {
return r;
}
}
case 'empty':
return r;
}
}
//# sourceMappingURL=substitutions.js.map