UNPKG

@informalsystems/quint

Version:

Core tool for the Quint specification language

903 lines (736 loc) 24.9 kB
import { describe, it } from 'mocha' import { assert } from 'chai' import { buildModuleWithDecls } from '../builders/ir' import { IRVisitor, walkModule } from '../../src/ir/IRVisitor' import { QuintDeclaration, QuintDef, QuintEx, QuintModule } from '../../src/ir/quintIr' import { declarationToString, definitionToString, expressionToString, moduleToString, typeToString, } from '../../src/ir/IRprinting' import { QuintType } from '../../src/ir/quintTypes' describe('walkModule', () => { const quintModule = buildModuleWithDecls([ 'var a: int', 'const B: int', 'type MY_TYPE = int', 'assume _ = N > 1', 'import M.*', 'import A(x = "rainbow") as A1', 'val f = S.filter(x => x + 1)', 'def l = val x = false { x }', ]) it('finds expressions', () => { class TestVisitor implements IRVisitor { entered: QuintEx[] = [] exited: QuintEx[] = [] enterExpr(expr: QuintEx): void { this.entered.push(expr) } exitExpr(expr: QuintEx): void { this.exited.push(expr) } } const enteredExpressions = [ 'igt(N, 1)', 'N', '1', '"rainbow"', 'filter(S, ((x) => iadd(x, 1)))', 'S', '((x) => iadd(x, 1))', 'iadd(x, 1)', 'x', '1', 'val x = false { x }', 'false', 'x', ] const exitedExpressions = [ 'N', '1', 'igt(N, 1)', '"rainbow"', 'S', 'x', '1', 'iadd(x, 1)', '((x) => iadd(x, 1))', 'filter(S, ((x) => iadd(x, 1)))', 'false', 'x', 'val x = false { x }', ] const visitor = new TestVisitor() walkModule(visitor, quintModule) assert.deepEqual(visitor.entered.map(expressionToString), enteredExpressions) assert.deepEqual(visitor.exited.map(expressionToString), exitedExpressions) }) it('finds declarations', () => { class TestVisitor implements IRVisitor { entered: QuintDeclaration[] = [] exited: QuintDeclaration[] = [] enterDecl(decl: QuintDeclaration): void { this.entered.push(decl) } exitDecl(decl: QuintDeclaration): void { this.exited.push(decl) } } const enteredDeclarations = [ 'var a: int', 'const B: int', 'type MY_TYPE = int', 'assume _ = igt(N, 1)', 'import M.*', 'import A(x = "rainbow") as A1', 'val f = filter(S, ((x) => iadd(x, 1)))', 'def l = val x = false { x }', ] const exitedDeclarations = [ 'var a: int', 'const B: int', 'type MY_TYPE = int', 'assume _ = igt(N, 1)', 'import M.*', 'import A(x = "rainbow") as A1', 'val f = filter(S, ((x) => iadd(x, 1)))', 'def l = val x = false { x }', ] const visitor = new TestVisitor() walkModule(visitor, quintModule) assert.deepEqual( visitor.entered.map(d => declarationToString(d)), enteredDeclarations ) assert.deepEqual( visitor.exited.map(d => declarationToString(d)), exitedDeclarations ) }) it('finds definitions', () => { class TestVisitor implements IRVisitor { entered: QuintDef[] = [] exited: QuintDef[] = [] enterDef(def: QuintDef): void { this.entered.push(def) } exitDef(def: QuintDef): void { this.exited.push(def) } } const enteredDefinitions = [ 'var a: int', 'const B: int', 'type MY_TYPE = int', 'assume _ = igt(N, 1)', 'val f = filter(S, ((x) => iadd(x, 1)))', 'def l = val x = false { x }', 'val x = false', // From the let definition ] const exitedDefinitions = [ 'var a: int', 'const B: int', 'type MY_TYPE = int', 'assume _ = igt(N, 1)', 'val f = filter(S, ((x) => iadd(x, 1)))', 'val x = false', // From the let definition 'def l = val x = false { x }', ] const visitor = new TestVisitor() walkModule(visitor, quintModule) assert.deepEqual( visitor.entered.map(d => definitionToString(d)), enteredDefinitions ) assert.deepEqual( visitor.exited.map(d => definitionToString(d)), exitedDefinitions ) }) it('finds types', () => { class TestVisitor implements IRVisitor { entered: QuintType[] = [] exited: QuintType[] = [] enterType(type: QuintType): void { this.entered.push(type) } exitType(type: QuintType): void { this.exited.push(type) } } const enteredTypes = [ 'int', // var a: int 'int', // const B: int 'int', // type MY_TYPE = int ] const exitedTypes = enteredTypes const visitor = new TestVisitor() walkModule(visitor, quintModule) // assert.deepEqual(visitor.entered.map(typeToString), enteredTypes) assert.deepEqual(visitor.exited.map(typeToString), exitedTypes) }) describe('visiting specific definitions', () => { it('finds operator definitions', () => { class TestVisitor implements IRVisitor { entered: QuintDeclaration[] = [] exited: QuintDeclaration[] = [] enterOpDef(def: QuintDeclaration): void { this.entered.push(def) } exitOpDef(def: QuintDeclaration): void { this.exited.push(def) } } const enteredDefinitions = [ 'val f = filter(S, ((x) => iadd(x, 1)))', 'def l = val x = false { x }', 'val x = false', // From the let definition ] const exitedDefinitions = [ 'val f = filter(S, ((x) => iadd(x, 1)))', 'val x = false', // From the let definition 'def l = val x = false { x }', ] const visitor = new TestVisitor() walkModule(visitor, quintModule) assert.deepEqual( visitor.entered.map(d => declarationToString(d)), enteredDefinitions ) assert.deepEqual( visitor.exited.map(d => declarationToString(d)), exitedDefinitions ) }) it('finds constant definitions', () => { class TestVisitor implements IRVisitor { entered: QuintDeclaration[] = [] exited: QuintDeclaration[] = [] enterConst(def: QuintDeclaration): void { this.entered.push(def) } exitConst(def: QuintDeclaration): void { this.exited.push(def) } } const enteredDefinitions = ['const B: int'] const exitedDefinitions = enteredDefinitions const visitor = new TestVisitor() walkModule(visitor, quintModule) assert.deepEqual( visitor.entered.map(d => declarationToString(d)), enteredDefinitions ) assert.deepEqual( visitor.exited.map(d => declarationToString(d)), exitedDefinitions ) }) it('finds variable definitions', () => { class TestVisitor implements IRVisitor { entered: QuintDeclaration[] = [] exited: QuintDeclaration[] = [] enterVar(def: QuintDeclaration): void { this.entered.push(def) } exitVar(def: QuintDeclaration): void { this.exited.push(def) } } const enteredDefinitions = ['var a: int'] const exitedDefinitions = enteredDefinitions const visitor = new TestVisitor() walkModule(visitor, quintModule) assert.deepEqual( visitor.entered.map(d => declarationToString(d)), enteredDefinitions ) assert.deepEqual( visitor.exited.map(d => declarationToString(d)), exitedDefinitions ) }) it('finds assume definitions', () => { class TestVisitor implements IRVisitor { entered: QuintDeclaration[] = [] exited: QuintDeclaration[] = [] enterAssume(def: QuintDeclaration): void { this.entered.push(def) } exitAssume(def: QuintDeclaration): void { this.exited.push(def) } } const enteredDefinitions = ['assume _ = igt(N, 1)'] const exitedDefinitions = enteredDefinitions const visitor = new TestVisitor() walkModule(visitor, quintModule) assert.deepEqual( visitor.entered.map(d => declarationToString(d)), enteredDefinitions ) assert.deepEqual( visitor.exited.map(d => declarationToString(d)), exitedDefinitions ) }) it('finds typedef definitions', () => { class TestVisitor implements IRVisitor { entered: QuintDeclaration[] = [] exited: QuintDeclaration[] = [] enterTypeDef(def: QuintDeclaration): void { this.entered.push(def) } exitTypeDef(def: QuintDeclaration): void { this.exited.push(def) } } const enteredDefinitions = ['type MY_TYPE = int'] const exitedDefinitions = enteredDefinitions const visitor = new TestVisitor() walkModule(visitor, quintModule) assert.deepEqual( visitor.entered.map(d => declarationToString(d)), enteredDefinitions ) assert.deepEqual( visitor.exited.map(d => declarationToString(d)), exitedDefinitions ) }) it('finds import definitions', () => { class TestVisitor implements IRVisitor { entered: QuintDeclaration[] = [] exited: QuintDeclaration[] = [] enterImport(def: QuintDeclaration): void { this.entered.push(def) } exitImport(def: QuintDeclaration): void { this.exited.push(def) } } const enteredDefinitions = ['import M.*'] const exitedDefinitions = enteredDefinitions const visitor = new TestVisitor() walkModule(visitor, quintModule) assert.deepEqual( visitor.entered.map(d => declarationToString(d)), enteredDefinitions ) assert.deepEqual( visitor.exited.map(d => declarationToString(d)), exitedDefinitions ) }) it('finds instance definitions', () => { class TestVisitor implements IRVisitor { entered: QuintDeclaration[] = [] exited: QuintDeclaration[] = [] enterInstance(def: QuintDeclaration): void { this.entered.push(def) } exitInstance(def: QuintDeclaration): void { this.exited.push(def) } } const enteredDefinitions = ['import A(x = "rainbow") as A1'] const exitedDefinitions = enteredDefinitions const visitor = new TestVisitor() walkModule(visitor, quintModule) assert.deepEqual( visitor.entered.map(d => declarationToString(d)), enteredDefinitions ) assert.deepEqual( visitor.exited.map(d => declarationToString(d)), exitedDefinitions ) }) it('finds the module itself', () => { class TestVisitor implements IRVisitor { entered: QuintModule[] = [] exited: QuintModule[] = [] enterModule(module: QuintModule): void { this.entered.push(module) } exitModule(module: QuintModule): void { this.exited.push(module) } } const enteredDefinitions = [ `module wrapper { var a: int const B: int type MY_TYPE = int assume _ = igt(N, 1) import M.* import A(x = "rainbow") as A1 val f = filter(S, ((x) => iadd(x, 1))) def l = val x = false { x } }`, ] const exitedDefinitions = enteredDefinitions const visitor = new TestVisitor() walkModule(visitor, quintModule) assert.deepEqual( visitor.entered.map(m => moduleToString(m)), enteredDefinitions ) assert.deepEqual( visitor.exited.map(m => moduleToString(m)), exitedDefinitions ) }) }) describe('visiting specific expressions', () => { it('finds name expressions', () => { class TestVisitor implements IRVisitor { entered: QuintEx[] = [] exited: QuintEx[] = [] enterName(expr: QuintEx): void { this.entered.push(expr) } exitName(expr: QuintEx): void { this.exited.push(expr) } } const enteredExpressions = ['N', 'S', 'x', 'x'] const exitedExpressions = enteredExpressions const visitor = new TestVisitor() walkModule(visitor, quintModule) assert.deepEqual(visitor.entered.map(expressionToString), enteredExpressions) assert.deepEqual(visitor.exited.map(expressionToString), exitedExpressions) }) it('finds literal expressions', () => { class TestVisitor implements IRVisitor { entered: QuintEx[] = [] exited: QuintEx[] = [] enterLiteral(expr: QuintEx): void { this.entered.push(expr) } exitLiteral(expr: QuintEx): void { this.exited.push(expr) } } const enteredExpressions = ['1', '"rainbow"', '1', 'false'] const exitedExpressions = enteredExpressions const visitor = new TestVisitor() walkModule(visitor, quintModule) assert.deepEqual(visitor.entered.map(expressionToString), enteredExpressions) assert.deepEqual(visitor.exited.map(expressionToString), exitedExpressions) }) it('finds application expressions', () => { class TestVisitor implements IRVisitor { entered: QuintEx[] = [] exited: QuintEx[] = [] enterApp(expr: QuintEx): void { this.entered.push(expr) } exitApp(expr: QuintEx): void { this.exited.push(expr) } } const enteredExpressions = ['igt(N, 1)', 'filter(S, ((x) => iadd(x, 1)))', 'iadd(x, 1)'] const exitedExpressions = ['igt(N, 1)', 'iadd(x, 1)', 'filter(S, ((x) => iadd(x, 1)))'] const visitor = new TestVisitor() walkModule(visitor, quintModule) assert.deepEqual(visitor.entered.map(expressionToString), enteredExpressions) assert.deepEqual(visitor.exited.map(expressionToString), exitedExpressions) }) it('finds lambda expressions', () => { class TestVisitor implements IRVisitor { entered: QuintEx[] = [] exited: QuintEx[] = [] enterLambda(expr: QuintEx): void { this.entered.push(expr) } exitLambda(expr: QuintEx): void { this.exited.push(expr) } } const enteredExpressions = ['((x) => iadd(x, 1))'] const exitedExpressions = enteredExpressions const visitor = new TestVisitor() walkModule(visitor, quintModule) assert.deepEqual(visitor.entered.map(expressionToString), enteredExpressions) assert.deepEqual(visitor.exited.map(expressionToString), exitedExpressions) }) it('finds let expressions', () => { class TestVisitor implements IRVisitor { entered: QuintEx[] = [] exited: QuintEx[] = [] enterLet(expr: QuintEx): void { this.entered.push(expr) } exitLet(expr: QuintEx): void { this.exited.push(expr) } } const enteredExpressions = ['val x = false { x }'] const exitedExpressions = enteredExpressions const visitor = new TestVisitor() walkModule(visitor, quintModule) assert.deepEqual(visitor.entered.map(expressionToString), enteredExpressions) assert.deepEqual(visitor.exited.map(expressionToString), exitedExpressions) }) }) describe('visiting specific types', () => { const quintModule = buildModuleWithDecls([ 'var a: bool', 'const b: int', 'val c: str = "rainbow"', 'var d: MY_CONST_TYPE', 'var e: a -> Set[a]', 'var f: Set[int]', 'var g: List[Set[str]]', 'var h: (int -> str) -> List[bool]', 'def i: (int, a) => bool = false', 'var j: (int, List[bool], MY_CONST_TYPE)', 'var k: { name: str, age: int }', ]) it('finds literal types', () => { class TestVisitor implements IRVisitor { entered: QuintType[] = [] exited: QuintType[] = [] enterLiteralType(type: QuintType): void { this.entered.push(type) } exitLiteralType(type: QuintType): void { this.exited.push(type) } } const enteredTypes = [ 'bool', // var a: bool 'int', // const b: int 'str', // val c: str = "rainbow" 'int', // var f: Set[int] 'str', // var g: List[Set[str]] 'int', // var h: (int -> str) -> List[bool] 'str', // var h: (int -> str) -> List[bool] 'bool', // var h: (int -> str) -> List[bool] 'int', // def i: (int, a) => bool = false 'bool', // def i: (int, a) => bool 'int', // var j: (int, List[bool], MY_CONST_TYPE) 'bool', // var j: (int, List[bool], MY_CONST_TYPE) 'str', // var k: { name: str, age: int } 'int', // var k: { name: str, age: int } ] const exitedTypes = enteredTypes const visitor = new TestVisitor() walkModule(visitor, quintModule) assert.deepEqual(visitor.entered.map(typeToString), enteredTypes) assert.deepEqual(visitor.exited.map(typeToString), exitedTypes) }) it('finds const types', () => { class TestVisitor implements IRVisitor { entered: QuintType[] = [] exited: QuintType[] = [] enterConstType(type: QuintType): void { this.entered.push(type) } exitConstType(type: QuintType): void { this.exited.push(type) } } const enteredTypes = [ 'MY_CONST_TYPE', // var d: MY_CONST_TYPE 'MY_CONST_TYPE', // var j: (int, list(bool), MY_CONST_TYPE) ] const exitedTypes = enteredTypes const visitor = new TestVisitor() walkModule(visitor, quintModule) assert.deepEqual(visitor.entered.map(typeToString), enteredTypes) assert.deepEqual(visitor.exited.map(typeToString), exitedTypes) }) it('finds var types', () => { class TestVisitor implements IRVisitor { entered: QuintType[] = [] exited: QuintType[] = [] enterVarType(type: QuintType): void { this.entered.push(type) } exitVarType(type: QuintType): void { this.exited.push(type) } } const enteredTypes = [ 'a', // var e: a -> Set[a] 'a', // var e: a -> Set[a] 'a', // def i: (int, a) => bool = false ] const exitedTypes = enteredTypes const visitor = new TestVisitor() walkModule(visitor, quintModule) assert.deepEqual(visitor.entered.map(typeToString), enteredTypes) assert.deepEqual(visitor.exited.map(typeToString), exitedTypes) }) it('finds set types', () => { class TestVisitor implements IRVisitor { entered: QuintType[] = [] exited: QuintType[] = [] enterSetType(type: QuintType): void { this.entered.push(type) } exitSetType(type: QuintType): void { this.exited.push(type) } } const enteredTypes = [ 'Set[a]', // var e: a -> Set[a] 'Set[int]', // var f: Set[int] 'Set[str]', // var g: List[Set[str]] ] const exitedTypes = enteredTypes const visitor = new TestVisitor() walkModule(visitor, quintModule) assert.deepEqual(visitor.entered.map(typeToString), enteredTypes) assert.deepEqual(visitor.exited.map(typeToString), exitedTypes) }) it('finds list types', () => { class TestVisitor implements IRVisitor { entered: QuintType[] = [] exited: QuintType[] = [] enterSeqType(type: QuintType): void { this.entered.push(type) } exitSeqType(type: QuintType): void { this.exited.push(type) } } const enteredTypes = [ 'List[Set[str]]', // var g: List[Set[str]] 'List[bool]', // var h: (int -> str) -> List[bool] 'List[bool]', // var j: (int, List[bool], MY_CONST_TYPE) ] const exitedTypes = enteredTypes const visitor = new TestVisitor() walkModule(visitor, quintModule) assert.deepEqual(visitor.entered.map(typeToString), enteredTypes) assert.deepEqual(visitor.exited.map(typeToString), exitedTypes) }) it('finds function types', () => { class TestVisitor implements IRVisitor { entered: QuintType[] = [] exited: QuintType[] = [] enterFunType(type: QuintType): void { this.entered.push(type) } exitFunType(type: QuintType): void { this.exited.push(type) } } const enteredTypes = [ '(a -> Set[a])', // var e: a -> Set[a] '((int -> str) -> List[bool])', // var h: (int -> str) -> List[bool] '(int -> str)', // var h: (int -> str) -> List[bool] ] const exitedTypes = [ '(a -> Set[a])', // var e: a -> Set[a] '(int -> str)', // var h: (int -> str) -> List[bool] '((int -> str) -> List[bool])', // var h: (int -> str) -> List[bool] ] const visitor = new TestVisitor() walkModule(visitor, quintModule) assert.deepEqual(visitor.entered.map(typeToString), enteredTypes) assert.deepEqual(visitor.exited.map(typeToString), exitedTypes) }) it('finds operator types', () => { class TestVisitor implements IRVisitor { entered: QuintType[] = [] exited: QuintType[] = [] enterOperType(type: QuintType): void { this.entered.push(type) } exitOperType(type: QuintType): void { this.exited.push(type) } } const enteredTypes = [ '(int, a) => bool', // def i: (int, a) => bool = false ] const exitedTypes = enteredTypes const visitor = new TestVisitor() walkModule(visitor, quintModule) assert.deepEqual(visitor.entered.map(typeToString), enteredTypes) assert.deepEqual(visitor.exited.map(typeToString), exitedTypes) }) it('finds tuple types', () => { class TestVisitor implements IRVisitor { entered: QuintType[] = [] exited: QuintType[] = [] enterTupleType(type: QuintType): void { this.entered.push(type) } exitTupleType(type: QuintType): void { this.exited.push(type) } } const enteredTypes = [ '(int, List[bool], MY_CONST_TYPE)', // var j: (int, list(bool), MY_CONST_TYPE) ] const exitedTypes = enteredTypes const visitor = new TestVisitor() walkModule(visitor, quintModule) assert.deepEqual(visitor.entered.map(typeToString), enteredTypes) assert.deepEqual(visitor.exited.map(typeToString), exitedTypes) }) it('finds record types', () => { class TestVisitor implements IRVisitor { entered: QuintType[] = [] exited: QuintType[] = [] enterRecordType(type: QuintType): void { this.entered.push(type) } exitRecordType(type: QuintType): void { this.exited.push(type) } } const enteredTypes = [ '{ name: str, age: int }', // var k: { name: str, age: int } ] const exitedTypes = enteredTypes const visitor = new TestVisitor() walkModule(visitor, quintModule) assert.deepEqual(visitor.entered.map(typeToString), enteredTypes) assert.deepEqual(visitor.exited.map(typeToString), exitedTypes) }) it('finds type applications', () => { const quintModule = buildModuleWithDecls(['val strMap: StrMap[int] = Map("a" -> 1, "b" -> 2)']) class TestVisitor implements IRVisitor { entered: QuintType[] = [] exited: QuintType[] = [] enterAppType(type: QuintType): void { this.entered.push(type) } exitAppType(type: QuintType): void { this.exited.push(type) } } const expectedTypes = ['StrMap[int]'] const visitor = new TestVisitor() walkModule(visitor, quintModule) assert.deepEqual(visitor.entered.map(typeToString), expectedTypes) assert.deepEqual(visitor.exited.map(typeToString), expectedTypes) }) it('finds parameter type annotations', () => { class TestVisitor implements IRVisitor { typesVisited: QuintType[] = [] exitType(t: QuintType) { this.typesVisited.push(t) } } const visitor = new TestVisitor() const m = buildModuleWithDecls(['def foo(x: int, b: str): bool = true']) walkModule(visitor, m) const actualTypes = visitor.typesVisited.map(typeToString) // `int` and `str` should each show up TWICE: // - once from of the lambda type annotations // - once from of the parameter type annotation const expectedTypes = ['int', 'str', 'bool', '(int, str) => bool', 'int', 'str'] assert.deepEqual(actualTypes, expectedTypes) }) }) })