@informalsystems/quint
Version:
Core tool for the Quint specification language
280 lines • 9.41 kB
JavaScript
;
/* ----------------------------------------------------------------------------------
* Copyright 2022 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.qualifierToString = exports.sumToString = exports.rowToString = exports.typeToString = exports.expressionToString = exports.definitionToString = exports.declarationToString = exports.moduleToString = void 0;
/**
* Pretty printing for IR components.
*
* @author Gabriela Moreira
*
* @module
*/
const quintIr_1 = require("./quintIr");
const quintTypes_1 = require("./quintTypes");
const printing_1 = require("../types/printing");
/**
* Pretty prints a module
*
* @param quintModule the Quint module to be formatted
*
* @returns a string with the pretty printed definition
*/
function moduleToString(quintModule) {
const defs = quintModule.declarations.map(d => declarationToString(d)).join('\n ');
return `module ${quintModule.name} {\n ${defs}\n}`;
}
exports.moduleToString = moduleToString;
/**
* Pretty prints a declaration. Includes a type annotation if the definition is
* annotated, or if a type is provided. A type annotation, if present, takes
* precedence over a type provided as argument to this function.
*
* @param decl the Quint declaration to be formatted
* @param includeBody optional, whether to include the body of the declaration,
* defaults to true
* @param type optional, the type scheme of the declaration, defaults to
* undefined
*
* @returns a string with the pretty printed declaration.
*/
function declarationToString(decl, includeBody = true, type) {
switch (decl.kind) {
case 'def':
case 'var':
case 'const':
case 'assume':
case 'typedef':
return definitionToString(decl, includeBody, type);
case 'import': {
let text = `import ${decl.protoName}`;
if (decl.defName) {
text += `.${decl.defName}`;
}
if (decl.qualifiedName) {
text += ` as ${decl.qualifiedName}`;
}
if (decl.fromSource) {
text += ` from "${decl.fromSource}"`;
}
return text;
}
case 'export': {
let text = `export ${decl.protoName}`;
if (decl.defName) {
text += `.${decl.defName}`;
}
if (decl.qualifiedName) {
text += ` as ${decl.qualifiedName}`;
}
return text;
}
case 'instance': {
const overrides = decl.overrides.map(o => `${o[0].name} = ${expressionToString(o[1])}`).join(', ');
let text = `import ${decl.protoName}(${overrides})`;
if (decl.qualifiedName) {
text += ` as ${decl.qualifiedName}`;
}
else {
text += `.*`;
}
if (decl.fromSource) {
text += ` from "${decl.fromSource}"`;
}
return text;
}
}
}
exports.declarationToString = declarationToString;
/**
* Pretty prints a definition. Includes a type annotation if the definition is
* annotated, or if a type is provided. The annotation is preferred over the
* type.
*
* @param def the Quint expression to be formatted
* @param includeBody optional, whether to include the body of the definition,
* defaults to true
* @param type optional, the type scheme of the definition, defaults to
* undefined
*
* @returns a string with the pretty printed definition.
*/
function definitionToString(def, includeBody = true, type) {
const typeAnnotation = (0, quintIr_1.isAnnotatedDef)(def)
? `: ${typeToString(def.typeAnnotation)}`
: type // If annotation is not present, but type is, use the type
? `: ${(0, printing_1.typeSchemeToString)(type)}`
: '';
switch (def.kind) {
case 'def': {
const header = `${qualifierToString(def.qualifier)} ${def.name}${typeAnnotation}`;
return includeBody ? `${header} = ${expressionToString(def.expr)}` : header;
}
case 'var':
return `var ${def.name}${typeAnnotation}`;
case 'const':
return `const ${def.name}${typeAnnotation}`;
case 'assume':
return `assume ${def.name} = ${expressionToString(def.assumption)}`;
case 'typedef':
if (def.type) {
const params = def.params && def.params.length > 0 ? `[${def.params.join(', ')}]` : '';
return `type ${def.name}${params} = ${typeToString(def.type)}`;
}
else {
return `type ${def.name}`;
}
}
}
exports.definitionToString = definitionToString;
/**
* Pretty prints an expression
*
* @param expr the Quint expression to be formatted
*
* @returns a string with the pretty printed expression
*/
function expressionToString(expr) {
switch (expr.kind) {
case 'name':
return expr.name;
case 'bool':
case 'int':
return expr.value.toString();
case 'str':
return `"${expr.value}"`;
case 'app':
return `${expr.opcode}(${expr.args.map(expressionToString).join(', ')})`;
case 'lambda':
return `((${expr.params.map(p => p.name).join(', ')}) => ${expressionToString(expr.expr)})`;
case 'let':
return `${declarationToString(expr.opdef)} { ${expressionToString(expr.expr)} }`;
}
}
exports.expressionToString = expressionToString;
/**
* Pretty prints a type
*
* @param type the Quint type to be formatted
*
* @returns a string with the pretty printed type
*/
function typeToString(type) {
switch (type.kind) {
case 'bool':
case 'int':
case 'str':
return type.kind;
case 'const':
case 'var':
return type.name;
case 'set':
return `Set[${typeToString(type.elem)}]`;
case 'list':
return `List[${typeToString(type.elem)}]`;
case 'fun':
return `(${typeToString(type.arg)} -> ${typeToString(type.res)})`;
case 'oper': {
const args = type.args.map(typeToString).join(', ');
return `(${args}) => ${typeToString(type.res)}`;
}
case 'tup':
return `(${rowFieldsToString(type.fields, false)})`;
case 'rec': {
return rowToString(type.fields);
}
case 'sum': {
return sumToString(type);
}
case 'app': {
const abs = typeToString(type.ctor);
const args = type.args.map(typeToString).join(', ');
return `${abs}[${args}]`;
}
}
}
exports.typeToString = typeToString;
/**
* Pretty prints a row type. Standard row printing used in error reporting
*
* @param r the row type to be formatted
*
* @returns a string with the pretty printed row
*/
function rowToString(r) {
const fields = rowFieldsToString(r);
return fields === '' ? '{}' : `{ ${fields} }`;
}
exports.rowToString = rowToString;
/**
* Pretty prints a sum type. Standard sum printing used in error reporting
*
* @param r the sum type to be formatted
*
* @returns a string with the pretty printed sum
*/
function sumToString(s) {
return '(' + sumFieldsToString(s.fields) + ')';
}
exports.sumToString = sumToString;
function sumFieldsToString(r) {
return (r.fields
.map((f) => {
if ((0, quintTypes_1.isUnitType)(f.fieldType)) {
return `${f.fieldName}`;
}
else {
return `${f.fieldName}(${typeToString(f.fieldType)})`;
}
})
// We are not exposing open rows in sum types currently
// So we do not show show row variables.
.concat(r.other.kind === 'row' ? [sumFieldsToString(r.other)] : [])
.join(' | '));
}
/**
* Pretty prints an operator qualifier.
*
* @param qualifier the qualidier to be formatted
*
* @returns a string with the pretty printed qualifier
*/
function qualifierToString(qualifier) {
switch (qualifier) {
case 'puredef':
return 'pure def';
case 'pureval':
return 'pure val';
default:
return qualifier;
}
}
exports.qualifierToString = qualifierToString;
function rowFieldsToString(r, showFieldName = true) {
switch (r.kind) {
case 'empty':
return '';
case 'var':
return `| ${r.name}`;
case 'row': {
const fields = r.fields.map(f => {
const prefix = showFieldName ? `${f.fieldName}: ` : '';
return `${prefix}${typeToString(f.fieldType)}`;
});
const other = rowFieldsToString(r.other);
switch (r.other.kind) {
case 'row':
fields.push(other);
return `${fields.join(', ')}`;
case 'var':
return `${fields.join(', ')} ${other}`;
case 'empty':
return `${fields.join(', ')}`;
}
}
}
}
//# sourceMappingURL=IRprinting.js.map