UNPKG

@informalsystems/quint

Version:

Core tool for the Quint specification language

484 lines 18 kB
"use strict"; /* ---------------------------------------------------------------------------------- * 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