@informalsystems/quint
Version:
Core tool for the Quint specification language
141 lines • 4.91 kB
JavaScript
"use strict";
/* ----------------------------------------------------------------------------------
* Copyright 2025 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.convertInit = void 0;
/**
* Convert init definitions to predicates to be compatible with TLA+.
* Returns errors if some action is re-used between init and step, as that would require generating new actions.
* We ask for user intervention in such edge cases.
*
* @author Gabriela Moreira
*
* @module
*/
const either_1 = require("@sweet-monads/either");
const IRTransformer_1 = require("./IRTransformer");
const IRVisitor_1 = require("./IRVisitor");
/**
* Converts the action named as "q::init" and all its dependencies to
* predicates (transforming assignments into equalities). If one of the
* converted dependencies is also used outside of init, this produces an error
* as that case would be more complicated to handle.
*
* @param module: the module with the init definition and its dependencies
* @param lookupTable: the lookup table for the module
* @param modes: the result of mode checking the module
*
* @returns The converted module, or errors instructing manual fix.
*/
function convertInit(module, lookupTable, modes) {
const defsFinder = new InitDefsFinder(lookupTable);
(0, IRVisitor_1.walkModule)(defsFinder, module);
const converter = new InitConverter(defsFinder.insideInitDefs, lookupTable, modes);
const convertedModule = (0, IRTransformer_1.transformModule)(converter, module);
if (converter.errors.length > 0) {
return (0, either_1.left)(converter.errors);
}
else {
return (0, either_1.right)(convertedModule);
}
}
exports.convertInit = convertInit;
class InitDefsFinder {
constructor(lookupTable) {
this.insideInitDefs = ['q::init'];
this.insideInit = 0;
this.lookupTable = lookupTable;
}
enterDef(def) {
if (this.insideInitDefs.includes(def.name)) {
this.insideInit++;
}
}
exitDef(def) {
if (this.insideInitDefs.includes(def.name)) {
this.insideInit--;
}
}
enterName(expr) {
if (this.insideInit > 0) {
const def = this.lookupTable.get(expr.id);
if (!def || def.kind != 'def') {
return;
}
this.insideInitDefs.push(expr.name);
(0, IRVisitor_1.walkDefinition)(this, def);
}
}
enterApp(app) {
if (this.insideInit > 0) {
const def = this.lookupTable.get(app.id);
if (!def || def.kind != 'def') {
return;
}
this.insideInitDefs.push(app.opcode);
(0, IRVisitor_1.walkDefinition)(this, def);
}
}
}
class InitConverter {
constructor(insideInitDefs, lookupTable, modes) {
this.errors = [];
this.insideInit = 0;
this.insideInitDefs = insideInitDefs;
this.lookupTable = lookupTable;
this.modes = modes;
}
enterDef(def) {
if (this.insideInitDefs.includes(def.name)) {
this.insideInit++;
}
return def;
}
exitDef(def) {
if (this.insideInitDefs.includes(def.name)) {
this.insideInit--;
}
return def;
}
enterApp(app) {
this.checkMixedUsage(app.id, app.opcode);
if (this.insideInit && app.opcode == 'assign') {
return { ...app, opcode: 'eq' };
}
return app;
}
enterName(expr) {
this.checkMixedUsage(expr.id, expr.name);
return expr;
}
checkMixedUsage(id, name) {
if (this.insideInit == 0 && this.insideInitDefs.includes(name) && this.hasAssignment(id)) {
this.errors.push({
code: 'QNT409',
message: `Action ${name} is used both for init and for step, and therefore can't be converted into TLA+. You can duplicate this with a different name to use on init. Sorry Quint can't do it for you yet.`,
reference: id,
});
}
}
hasAssignment(id) {
const def = this.lookupTable.get(id);
if (!def) {
return false;
}
const mode = this.modes.get(def.id);
if (mode == 'action') {
// The mode checker says the mode should be action
return true;
}
if (mode == undefined) {
// The mode checker doesn't have suggestions, so the annotation is correct
return def.kind == 'def' && def.qualifier == 'action';
}
// Mode is not action
return false;
}
}
//# sourceMappingURL=initToPredicate.js.map