UNPKG

@informalsystems/quint

Version:

Core tool for the Quint specification language

193 lines (152 loc) 6.28 kB
import { describe, it } from 'mocha' import { assert } from 'chai' import { parseTypeOrThrow } from '../../src/types/parser' import { Substitutions, applySubstitution, applySubstitutionToConstraint, compose } from '../../src/types/substitutions' import { Constraint } from '../../src/types/base' import { constraintToString } from '../../src/types/printing' import { Row } from '../../src' import { LookupTable } from '../../src/names/base' const table: LookupTable = new Map() describe('compose', () => { it('composes two substitutions', () => { const row: Row = { kind: 'row', fields: [{ fieldName: 'f', fieldType: parseTypeOrThrow('bool') }], other: { kind: 'empty' }, } const s1: Substitutions = [ { kind: 'type', name: 'a', value: parseTypeOrThrow('int'), }, ] const s2: Substitutions = [ { kind: 'row', name: 'b', value: row, }, ] const result = compose(table, s1, s2) assert.sameDeepMembers(result, [ { kind: 'type', name: 'a', value: parseTypeOrThrow('int') }, { kind: 'row', name: 'b', value: row }, ]) }) it('unifies values of substitutions with same name', () => { const s1: Substitutions = [{ kind: 'type', name: 'v1', value: parseTypeOrThrow('int') }] const s2: Substitutions = [{ kind: 'type', name: 'v1', value: { kind: 'var', name: 'q' } }] const result = compose(table, s1, s2) assert.sameDeepMembers(result, s1.concat([{ kind: 'type', name: 'q', value: parseTypeOrThrow('int') }])) }) }) describe('applySubstitution', () => { it('substitutes variables in arrow type', () => { const s: Substitutions = [ { kind: 'type', name: 'a', value: { kind: 'int', id: 1n } }, { kind: 'type', name: 'b', value: { kind: 'bool', id: 2n } }, ] const t = parseTypeOrThrow('(a) => b') const result = applySubstitution(table, s, t) assert.deepEqual(result, parseTypeOrThrow('(int) => bool')) }) it('substitutes variables in function type', () => { const s: Substitutions = [ { kind: 'type', name: 'a', value: { kind: 'int', id: 1n } }, { kind: 'type', name: 'b', value: { kind: 'bool', id: 2n } }, ] const t = parseTypeOrThrow('a -> b') const result = applySubstitution(table, s, t) assert.deepEqual(result, parseTypeOrThrow('int -> bool')) }) it('substitutes variables in lists, sets and tuples types', () => { const s: Substitutions = [ { kind: 'type', name: 'a', value: { kind: 'int', id: 1n } }, { kind: 'type', name: 'b', value: { kind: 'bool', id: 3n } }, { kind: 'type', name: 'c', value: { kind: 'str', id: 5n } }, ] const t = parseTypeOrThrow('(List[a], Set[b], c)') const result = applySubstitution(table, s, t) assert.deepEqual(result, parseTypeOrThrow('(List[int], Set[bool], str)')) }) it('substitutes variables in record type', () => { const s: Substitutions = [ { kind: 'type', name: 'a', value: { kind: 'int', id: 1n } }, { kind: 'type', name: 'b', value: { kind: 'bool', id: 2n } }, ] const t = parseTypeOrThrow('{ a: a, b: b }') const result = applySubstitution(table, s, t) assert.deepEqual(result, parseTypeOrThrow('{ a: int, b: bool }')) }) it('substitutes variables in record type with row variable', () => { const s: Substitutions = [ { kind: 'type', name: 'a', value: { kind: 'int', id: 1n } }, { kind: 'type', name: 'b', value: { kind: 'bool', id: 2n } }, { kind: 'row', name: 'r', value: { kind: 'empty' } }, ] const t = parseTypeOrThrow('{ a: a, b: b | r }') const result = applySubstitution(table, s, t) assert.deepEqual(result, parseTypeOrThrow('{ a: int, b: bool }')) }) it('substitutes with transitivity', () => { const s: Substitutions = [ { kind: 'type', name: 'a', value: { kind: 'var', id: 1n, name: 'b' } }, { kind: 'type', name: 'b', value: { kind: 'bool', id: 2n } }, ] const t = parseTypeOrThrow('a') const result = applySubstitution(table, s, t) assert.deepEqual(result, { kind: 'bool', id: 2n }) }) }) describe('applySubstitutionToConstraint', () => { const s: Substitutions = [ { kind: 'type', name: 'a', value: { kind: 'int', id: 1n } }, { kind: 'type', name: 'b', value: { kind: 'bool', id: 1n } }, ] it('applies substitution to types in equality constraint', () => { const t1 = parseTypeOrThrow('a') const t2 = parseTypeOrThrow('b') const result = applySubstitutionToConstraint(table, s, { kind: 'eq', types: [t1, t2], sourceId: 1n }) const expectedResult: Constraint = { kind: 'eq', types: [parseTypeOrThrow('int'), parseTypeOrThrow('bool')], sourceId: 1n, } assert.deepEqual( result, expectedResult, `expected ${constraintToString(expectedResult)}, got ${constraintToString(result)}` ) }) it('does nothing to empty constraint', () => { const result = applySubstitutionToConstraint(table, s, { kind: 'empty' }) const expectedResult: Constraint = { kind: 'empty' } assert.deepEqual( result, expectedResult, `expected ${constraintToString(expectedResult)}, got ${constraintToString(result)}` ) }) it('applies substitution recursively to constraints in conjunction', () => { const c1: Constraint = { kind: 'eq', types: [parseTypeOrThrow('a'), parseTypeOrThrow('b')], sourceId: 1n } const c2: Constraint = { kind: 'eq', types: [parseTypeOrThrow('b'), parseTypeOrThrow('b')], sourceId: 1n } const result = applySubstitutionToConstraint(table, s, { kind: 'conjunction', constraints: [c1, c2], sourceId: 1n }) const expected1: Constraint = { kind: 'eq', types: [parseTypeOrThrow('int'), parseTypeOrThrow('bool')], sourceId: 1n, } const expected2: Constraint = { kind: 'eq', types: [parseTypeOrThrow('bool'), parseTypeOrThrow('bool')], sourceId: 1n, } const expectedResult: Constraint = { kind: 'conjunction', constraints: [expected1, expected2], sourceId: 1n } assert.deepEqual( result, expectedResult, `expected ${constraintToString(expectedResult)}, got ${constraintToString(result)}` ) }) })