UNPKG

@informalsystems/quint

Version:

Core tool for the Quint specification language

146 lines 5.77 kB
"use strict"; /* ---------------------------------------------------------------------------------- * Copyright 2022-2023 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.NameResolver = exports.resolveNames = void 0; /** * Name resolution for Quint modules. * * @author Gabriela Moreira * * @module */ const IRVisitor_1 = require("../ir/IRVisitor"); const base_1 = require("./base"); const collector_1 = require("./collector"); const lodash_1 = require("lodash"); /** * Resolves all names in the given Quint modules and returns a lookup table of definitions. * * @param quintModules - The Quint modules to resolve names in. * * @returns A lookup table of definitions and a mapping of unused definitions if successful, otherwise a list of errors. */ function resolveNames(quintModules) { const visitor = new NameResolver(); quintModules.forEach(module => { (0, IRVisitor_1.walkModule)(visitor, module); }); return { table: visitor.table, unusedDefinitions: visitor.unusedDefinitions, errors: visitor.errors, resolver: visitor, }; } exports.resolveNames = resolveNames; /** * `NameResolver` uses `NameCollector` to collect top-level definitions. Scoped * definitions are collected inside of `NameResolver` as it navigates the IR. */ class NameResolver { constructor() { this.errors = []; this.table = new Map(); // the current depth of operator definitions: top-level defs are depth 0 // FIXME(#1279): The walk* functions update this value, but they need to be // initialized to -1 here for that to work on all scenarios. this.definitionDepth = -1; this.unusedDefinitions = moduleName => { const definitions = Array.from(this.collector.definitionsByModule.get(moduleName)?.values() || []).flat(); const usedDefinitions = [...this.table.values()].flat(); return new Set((0, lodash_1.difference)(definitions, usedDefinitions)); }; this.collector = new collector_1.NameCollector(); // bind the errors so they are aggregated in the same array this.collector.errors = this.errors; } switchToModule(moduleName) { this.collector.switchToModule(moduleName); } enterModule(module) { // First thing to do in resolving names for a module is to collect all // top-level definitions for that module. This has to be done in a separate // pass because names can appear before they are defined. (0, IRVisitor_1.walkModule)(this.collector, module); } enterOpDef(def) { // Top-level definitions were already collected, so we only need to collect // scoped definitions. if (this.definitionDepth > 0) { const newDef = this.collector.collectDefinition({ ...def, depth: this.definitionDepth }); this.table.set(def.id, { ...newDef, depth: this.definitionDepth }); } else { // Map the definition to itself so we can recover depth information from the table this.table.set(def.id, { ...def, depth: this.definitionDepth }); } } exitLet(expr) { // When exiting a let, delete the operator definition so it is not accessed // outside of the let scope this.collector.deleteDefinition(expr.opdef.name); } enterLambda(expr) { // Lambda parameters are scoped, so they are collected here expr.params.forEach(p => { this.collector.collectDefinition({ ...p, kind: 'param', depth: this.definitionDepth }); }); } exitLambda(expr) { // Similar to let, delete the parameter definitions when exiting the lambda // so they are not accessed outside of the lambda scope expr.params.forEach(p => { this.collector.deleteDefinition(p.name); }); } enterName(nameExpr) { // Name expression, check that the name is defined this.resolveName(nameExpr.name, nameExpr.id); } enterApp(appExpr) { // Application, check that the operator being applied is defined this.resolveName(appExpr.opcode, appExpr.id); } enterConstType(type) { // Type is a name, check that it is defined const def = this.collector.getDefinition(type.name); if (!def || def.kind !== 'typedef') { this.recordNameError('type', type.name, type.id); return; } this.table.set(type.id, def); } enterInstance(def) { // Resolve overridden param names in the current module def.overrides.forEach(([param, _]) => { const qualifiedName = def.qualifiedName ? `${def.qualifiedName}::${param.name}` : param.name; this.resolveName(qualifiedName, param.id); }); } resolveName(name, id) { if (base_1.builtinNames.includes(name)) { return; } const def = this.collector.getDefinition(name); if (!def || def.kind === 'typedef') { this.recordNameError('name', name, id); return; } this.table.set(id, def); } recordNameError(kind, name, id) { const description = kind === 'name' ? 'Name' : 'Type alias'; this.errors.push({ code: 'QNT404', message: `${description} '${name}' not found`, reference: id, data: {}, }); } } exports.NameResolver = NameResolver; //# sourceMappingURL=resolver.js.map