@informalsystems/quint
Version:
Core tool for the Quint specification language
484 lines • 18 kB
JavaScript
;
/* ----------------------------------------------------------------------------------
* Copyright 2023 Informal Systems
* Licensed under the Apache License, Version 2.0.
* See LICENSE in the project root for license information.
* --------------------------------------------------------------------------------- */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.transformRow = exports.transformDefinition = exports.transformDeclaration = exports.transformLookupDefinition = exports.transformType = exports.transformModule = exports.IRTransformer = void 0;
/**
* Visitor pattern-like implementation for Quint IR components. Use this to
* transform the IR instead of implementing a recursion over it yourself.
*
* @author Gabriela Moreira
*
* @module
*/
const ir = __importStar(require("./quintIr"));
const util_1 = require("../util");
const lodash_clonedeep_1 = __importDefault(require("lodash.clonedeep"));
class IRTransformer {
}
exports.IRTransformer = IRTransformer;
/**
* Navigates a Quint module with a transformer, invoking the correspondent function for each
* found IR component.
*
* @param transformer: the IRTransformer instance with the functions to be invoked
* @param quintModule: the Quint module to be navigated
*
* @returns the transformed Quint module
*/
function transformModule(transformer, quintModule) {
let newModule = (0, lodash_clonedeep_1.default)(quintModule);
if (transformer.enterModule) {
newModule = transformer.enterModule(newModule);
}
newModule.declarations = newModule.declarations.map(decl => transformDeclaration(transformer, decl));
if (transformer.exitModule) {
newModule = transformer.exitModule(newModule);
}
return newModule;
}
exports.transformModule = transformModule;
/**
* Navigates a Quint type with a transformer, invoking the correspondent function for each
* inner type.
*
* @param transformer: the IRTransformer instance with the functions to be invoked
* @param type: the Quint type to be navigated
*
* @returns the transformed Quint type
*/
function transformType(transformer, type) {
let newType = (0, lodash_clonedeep_1.default)(type);
if (transformer.enterType) {
newType = transformer.enterType(newType);
}
switch (newType.kind) {
case 'bool':
case 'int':
case 'str':
if (transformer.enterLiteralType) {
newType = transformer.enterLiteralType(newType);
}
if (transformer.exitLiteralType) {
newType = transformer.exitLiteralType(newType);
}
break;
case 'const':
if (transformer.enterConstType) {
newType = transformer.enterConstType(newType);
}
if (transformer.exitConstType) {
newType = transformer.exitConstType(newType);
}
break;
case 'var':
if (transformer.enterVarType) {
newType = transformer.enterVarType(newType);
}
if (transformer.exitVarType) {
newType = transformer.exitVarType(newType);
}
break;
case 'set':
if (transformer.enterSetType) {
newType = transformer.enterSetType(newType);
}
newType.elem = transformType(transformer, newType.elem);
if (transformer.exitSetType) {
newType = transformer.exitSetType(newType);
}
break;
case 'list':
if (transformer.enterSeqType) {
newType = transformer.enterSeqType(newType);
}
newType.elem = transformType(transformer, newType.elem);
if (transformer.exitSeqType) {
newType = transformer.exitSeqType(newType);
}
break;
case 'fun':
if (transformer.enterFunType) {
newType = transformer.enterFunType(newType);
}
// Functions, transform both argument and result
newType.arg = transformType(transformer, newType.arg);
newType.res = transformType(transformer, newType.res);
if (transformer.exitFunType) {
newType = transformer.exitFunType(newType);
}
break;
case 'oper':
if (transformer.enterOperType) {
transformer.enterOperType(newType);
}
// Operators, transform all arguments and result
newType.args = newType.args.map(arg => transformType(transformer, arg));
newType.res = transformType(transformer, newType.res);
if (transformer.exitOperType) {
newType = transformer.exitOperType(newType);
}
break;
case 'tup':
if (transformer.enterTupleType) {
newType = transformer.enterTupleType(newType);
}
// Tuples, transform all elements
newType.fields = transformRow(transformer, newType.fields);
if (transformer.exitTupleType) {
newType = transformer.exitTupleType(newType);
}
break;
case 'rec':
if (transformer.enterRecordType) {
newType = transformer.enterRecordType(newType);
}
// Records, transform all fields
newType.fields = transformRow(transformer, newType.fields);
if (transformer.exitRecordType) {
newType = transformer.exitRecordType(newType);
}
break;
case 'sum':
{
if (transformer.enterSumType) {
newType = transformer.enterSumType(newType);
}
// Sum types, transform all types
const newFields = transformRow(transformer, newType.fields);
if (newFields.kind !== 'row') {
throw new Error('Impossible: sum type fields transformed into non-row');
}
newType.fields = newFields;
if (transformer.exitSumType) {
newType = transformer.exitSumType(newType);
}
}
break;
case 'app':
{
if (transformer.enterAppType) {
newType = transformer.enterAppType(newType);
}
newType.args = newType.args.map(v => transformType(transformer, v));
if (transformer.exitAppType) {
newType = transformer.exitAppType(newType);
}
}
break;
default:
(0, util_1.unreachable)(newType);
}
if (transformer.exitType) {
newType = transformer.exitType(newType);
}
return newType;
}
exports.transformType = transformType;
/**
* Transforms a Quint LookupDefinition with a transformer
*
* This is just a thin wrapper to deal with the fact that LookupDefinitions are a slightly awkward union.
*
* @param transformer: the IRTransformer instance with the functions to be invoked
* @param lud: the Quint LookupDefinition to be transformed
*
* @returns the transformed LookupDefinition
*/
function transformLookupDefinition(transformer, lud) {
switch (lud.kind) {
case 'const':
case 'def':
case 'var':
case 'assume':
case 'typedef':
return transformDefinition(transformer, lud);
case 'param':
return lud.typeAnnotation ? { ...lud, typeAnnotation: transformType(transformer, lud.typeAnnotation) } : lud;
}
}
exports.transformLookupDefinition = transformLookupDefinition;
/**
* Transforms a Quint declaration with a transformer, invoking the corresponding function for each
* inner component.
*
* @param transformer: the IRTransformer instance with the functions to be invoked
* @param decl: the Quint declaration to be transformed
*
* @returns the transformed Quint definition
*/
function transformDeclaration(transformer, decl) {
let newDecl = (0, lodash_clonedeep_1.default)(decl);
if (transformer.enterDecl) {
newDecl = transformer.enterDecl(newDecl);
}
switch (newDecl.kind) {
case 'instance':
if (transformer.enterInstance) {
newDecl = transformer.enterInstance(newDecl);
}
newDecl.overrides = newDecl.overrides.map(([i, e]) => [i, transformExpression(transformer, e)]);
if (transformer.exitInstance) {
newDecl = transformer.exitInstance(newDecl);
}
break;
case 'import':
if (transformer.enterImport) {
newDecl = transformer.enterImport(newDecl);
}
if (transformer.exitImport) {
newDecl = transformer.exitImport(newDecl);
}
break;
case 'export':
if (transformer.enterExport) {
newDecl = transformer.enterExport(newDecl);
}
if (transformer.exitExport) {
newDecl = transformer.exitExport(newDecl);
}
break;
case 'const':
case 'var':
case 'def':
case 'typedef':
case 'assume':
newDecl = transformDefinition(transformer, newDecl);
break;
default:
(0, util_1.unreachable)(newDecl);
}
if (transformer.exitDecl) {
newDecl = transformer.exitDecl(newDecl);
}
return newDecl;
}
exports.transformDeclaration = transformDeclaration;
/**
* Transforms a Quint definition with a transformer, invoking the correspondent function for each
* inner component.
*
* @param transformer: the IRTransformer instance with the functions to be invoked
* @param def: the Quint definition to be transformed
*
* @returns the transformed Quint definition
*/
function transformDefinition(transformer, def) {
let newDef = (0, lodash_clonedeep_1.default)(def);
if (transformer.enterDef) {
newDef = transformer.enterDef(newDef);
}
if (ir.isAnnotatedDef(newDef)) {
newDef.typeAnnotation = transformType(transformer, newDef.typeAnnotation);
}
else if (ir.isTypeAlias(newDef)) {
newDef.type = transformType(transformer, newDef.type);
}
switch (newDef.kind) {
case 'const':
if (transformer.enterConst) {
newDef = transformer.enterConst(newDef);
}
if (transformer.exitConst) {
newDef = transformer.exitConst(newDef);
}
break;
case 'var':
if (transformer.enterVar) {
newDef = transformer.enterVar(newDef);
}
if (transformer.exitVar) {
newDef = transformer.exitVar(newDef);
}
break;
case 'def':
if (transformer.enterOpDef) {
newDef = transformer.enterOpDef(newDef);
}
newDef.expr = transformExpression(transformer, newDef.expr);
if (transformer.exitOpDef) {
newDef = transformer.exitOpDef(newDef);
}
break;
case 'typedef':
if (transformer.enterTypeDef) {
newDef = transformer.enterTypeDef(newDef);
}
if (transformer.exitTypeDef) {
newDef = transformer.exitTypeDef(newDef);
}
break;
case 'assume':
if (transformer.enterAssume) {
newDef = transformer.enterAssume(newDef);
}
newDef.assumption = transformExpression(transformer, newDef.assumption);
if (transformer.exitAssume) {
newDef = transformer.exitAssume(newDef);
}
break;
default:
(0, util_1.unreachable)(newDef);
}
if (transformer.exitDef) {
newDef = transformer.exitDef(newDef);
}
return newDef;
}
exports.transformDefinition = transformDefinition;
/**
* Transforms a Quint expression with a transformer, invoking the correspondent function for each
* inner component.
*
* @param transformer: the IRTransformer instance with the functions to be invoked
* @param expr: the Quint expression to be transformed
*
* @returns the transformed Quint expression
*/
function transformExpression(transformer, expr) {
let newExpr = (0, lodash_clonedeep_1.default)(expr);
if (transformer.enterExpr) {
newExpr = transformer.enterExpr(newExpr);
}
switch (newExpr.kind) {
case 'name':
if (transformer.enterName) {
newExpr = transformer.enterName(newExpr);
}
if (transformer.exitName) {
newExpr = transformer.exitName(newExpr);
}
break;
case 'bool':
case 'int':
case 'str':
if (transformer.enterLiteral) {
newExpr = transformer.enterLiteral(newExpr);
}
if (transformer.exitLiteral) {
newExpr = transformer.exitLiteral(newExpr);
}
break;
case 'app': {
if (transformer.enterApp) {
newExpr = transformer.enterApp(newExpr);
}
newExpr.args = newExpr.args.map(arg => transformExpression(transformer, arg));
if (transformer.exitApp) {
newExpr = transformer.exitApp(newExpr);
}
break;
}
case 'lambda':
if (transformer.enterLambda) {
newExpr = transformer.enterLambda(newExpr);
}
newExpr.params = newExpr.params.map(p => p.typeAnnotation ? { ...p, typeAnnotation: transformType(transformer, p.typeAnnotation) } : p);
newExpr.expr = transformExpression(transformer, newExpr.expr);
if (transformer.exitLambda) {
newExpr = transformer.exitLambda(newExpr);
}
break;
case 'let':
{
if (transformer.enterLet) {
newExpr = transformer.enterLet(newExpr);
}
const opdef = transformDefinition(transformer, newExpr.opdef);
if (opdef.kind !== 'def') {
// This should only happen if we write a bad transformer. Should never
// be a user facing issue.
throw new Error('Let operator definition transformed into non-operator definition');
}
newExpr.opdef = opdef;
newExpr.expr = transformExpression(transformer, newExpr.expr);
if (transformer.exitLet) {
newExpr = transformer.exitLet(newExpr);
}
}
break;
default:
(0, util_1.unreachable)(newExpr);
}
if (transformer.exitExpr) {
newExpr = transformer.exitExpr(newExpr);
}
return newExpr;
}
/**
* Transforms a Quint row with a transformer, invoking the correspondent function for each
* inner component.
*
* @param transformer: the IRTransformer instance with the functions to be invoked
* @param row: the Quint row to be transformed
*
* @returns the transformed Quint row
*/
function transformRow(transformer, row) {
let newRow = (0, lodash_clonedeep_1.default)(row);
if (transformer.enterRow) {
newRow = transformer.enterRow(newRow);
}
switch (newRow.kind) {
case 'row':
if (transformer.enterConcreteRow) {
newRow = transformer.enterConcreteRow(newRow);
}
newRow.fields = newRow.fields.map(field => ({ ...field, fieldType: transformType(transformer, field.fieldType) }));
newRow.other = transformRow(transformer, newRow.other);
if (transformer.exitConcreteRow) {
newRow = transformer.exitConcreteRow(newRow);
}
break;
case 'var':
if (transformer.enterVarRow) {
newRow = transformer.enterVarRow(newRow);
}
if (transformer.exitVarRow) {
newRow = transformer.exitVarRow(newRow);
}
break;
case 'empty':
if (transformer.enterEmptyRow) {
newRow = transformer.enterEmptyRow(newRow);
}
if (transformer.exitEmptyRow) {
newRow = transformer.exitEmptyRow(newRow);
}
}
if (transformer.exitRow) {
newRow = transformer.exitRow(newRow);
}
return newRow;
}
exports.transformRow = transformRow;
//# sourceMappingURL=IRTransformer.js.map