@informalsystems/quint
Version:
Core tool for the Quint specification language
164 lines • 6.34 kB
JavaScript
"use strict";
/* ----------------------------------------------------------------------------------
* Copyright 2022-2024 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.VarStorage = exports.initialRegisterValue = void 0;
/**
* A storage to keep track of Quint state variables in the current and next state.
*
* @author Gabriela Moreira
*
* @module
*/
const either_1 = require("@sweet-monads/either");
const runtimeValue_1 = require("./runtimeValue");
const immutable_1 = require("immutable");
const itf_1 = require("../../itf");
/**
* Initializes the register value for a given variable name.
*
* @param name - The name of the variable to initialize, to be used in error messages
* @returns a QuintError indicating the variable is not set
*/
function initialRegisterValue(name) {
return (0, either_1.left)({ code: 'QNT502', message: `Variable ${name} not set` });
}
exports.initialRegisterValue = initialRegisterValue;
/**
* A storage to keep track of Quint state variables in the current and next state.
*/
class VarStorage {
/**
* Constructs a new VarStorage instance.
*
* @param storeMetadata - Indicates whether to store metadata.
* @param nondetPicks - Non-deterministic picks and their values for the current step. Should be
the one constructed in the builder.
*/
constructor(storeMetadata, nondetPicks) {
/**
* An immutable map with registers for the current state variables.
*/
this.vars = (0, immutable_1.Map)();
/**
* An immutable map with registers for the next state variables.
*/
this.nextVars = (0, immutable_1.Map)();
/**
* Non-deterministic picks and their values for the current step.
*/
this.nondetPicks = new Map();
/**
* Cached values that need to be cleared when shifting.
*/
this.cachesToClear = [];
this.storeMetadata = storeMetadata;
this.nondetPicks = nondetPicks;
}
/**
* Shifts the current state variables to the next state variables.
* This method updates the current state variable registers with the values
* from the next state variable registers, initializes the next state variable
* registers, and clears cached values.
*/
shiftVars() {
this.vars.forEach((reg, key) => {
reg.value = this.nextVars.get(key)?.value ?? initialRegisterValue(reg.name);
});
this.nextVars.forEach(reg => (reg.value = initialRegisterValue(reg.name)));
this.clearCaches();
}
/**
* Converts the current state variables and metadata into a RuntimeValue record.
*
* @returns A RuntimeValue representing the current state variables and metadata.
*/
asRecord() {
const map = this.vars
.valueSeq()
.toArray()
.filter(r => r.value.isRight())
.map(r => [r.name, r.value.unwrap()]);
if (this.storeMetadata) {
const nondetPicksRecord = runtimeValue_1.rv.mkRecord([...this.nondetPicks.entries()].map(([name, value]) => {
const valueVariant = value ? runtimeValue_1.rv.mkVariant('Some', value) : runtimeValue_1.rv.mkVariant('None', runtimeValue_1.rv.mkTuple([]));
return [name, valueVariant];
}));
map.push([itf_1.NONDET_PICKS, nondetPicksRecord]);
map.push([itf_1.ACTION_TAKEN, runtimeValue_1.rv.mkStr(this.actionTaken ?? '')]);
}
return runtimeValue_1.rv.mkRecord(map);
}
fromRecord(record) {
this.reset();
record.toOrderedMap().forEach((value, key) => {
const regToSet = this.vars
.valueSeq()
.toArray()
.find(r => r.name === key);
if (regToSet != undefined) {
regToSet.value = (0, either_1.right)(value);
}
else if (key === itf_1.NONDET_PICKS && this.storeMetadata) {
value.toOrderedMap().forEach((v, k) => {
this.nondetPicks.set(k, v.toVariant()[0] == 'None' ? undefined : v);
});
}
else if (key === itf_1.ACTION_TAKEN && this.storeMetadata) {
this.actionTaken = value.toStr();
}
});
this.clearCaches();
}
/**
* Resets the current and next state variable registers to their initial values.
* This method sets the value of each register in both the current and next state
* variable maps to an undefined (error) value.
*/
reset() {
this.vars.forEach(reg => (reg.value = initialRegisterValue(reg.name)));
this.nextVars.forEach(reg => (reg.value = initialRegisterValue(reg.name)));
if (this.storeMetadata) {
this.actionTaken = undefined;
this.nondetPicks.forEach((_, key) => {
this.nondetPicks.set(key, undefined);
});
}
}
/**
* Creates a snapshot of the current state of the VarStorage, with the relevant information to backtrack.
* @returns A snapshot of the current state of the VarStorage.
*/
snapshot() {
return {
nextVars: this.nextVars.map(reg => ({ ...reg })),
nondetPicks: new Map(this.nondetPicks),
actionTaken: this.actionTaken,
};
}
/**
* Recovers the state of the VarStorage from a snapshot.
*
* @param snapshot - the snapshot to recover the state from
*/
recoverSnapshot(snapshot) {
this.nextVars.forEach((reg, key) => {
const snapshotReg = snapshot.nextVars.get(key);
if (snapshotReg) {
reg.value = snapshotReg.value;
}
});
this.nondetPicks = snapshot.nondetPicks;
this.actionTaken = snapshot.actionTaken;
}
clearCaches() {
this.cachesToClear.forEach(cachedValue => {
cachedValue.value = undefined;
});
}
}
exports.VarStorage = VarStorage;
//# sourceMappingURL=VarStorage.js.map