├── .gitignore ├── LICENSE.md ├── Makefile ├── README.md ├── ast.go ├── ast_arith.go ├── ast_arith_test.go ├── ast_logic.go ├── ast_logic_test.go ├── config.go ├── config_test.go ├── context.go ├── context_test.go ├── enum.go ├── error.go ├── error_test.go ├── go-z3.h ├── model.go ├── model_test.go ├── solver.go ├── solver_test.go ├── sort.go ├── symbol.go ├── symbol_test.go ├── z3.go └── z3_examples_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | libz3.a 2 | vendor/ 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Mitchell Hashimoto 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | Z3_REF ?= master 2 | 3 | all: libz3.a test 4 | 5 | clean: 6 | rm -rf vendor 7 | rm -f libz3.a 8 | 9 | gofmt: 10 | @echo "Checking code with gofmt.." 11 | gofmt -s *.go >/dev/null 12 | 13 | libz3.a: vendor/z3 14 | cd vendor/z3 && python scripts/mk_make.py --staticlib 15 | cd vendor/z3/build && ${MAKE} 16 | cp vendor/z3/build/libz3.a . 17 | 18 | vendor/z3: 19 | mkdir -p vendor 20 | git clone https://github.com/Z3Prover/z3.git vendor/z3 21 | cd vendor/z3 && git reset --hard && git clean -fdx 22 | cd vendor/z3 && git checkout ${Z3_REF} 23 | 24 | test: gofmt 25 | go test -v 26 | 27 | .PHONY: all clean libz3.a test 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Go Bindings to the Z3 Theorem Prover 2 | 3 | go-z3 provides bindings to [Z3](https://github.com/Z3Prover/z3), a 4 | theorem prover out of Microsoft Research. Z3 is a state-of-the-art 5 | [SMT Solver](https://en.wikipedia.org/wiki/Satisfiability_modulo_theories). 6 | 7 | This library provides bindings via cgo directly to Z3. 8 | 9 | **Library Status:** Most major sections of the API are covered, but the 10 | Z3 API is massive. Adding missing APIs should be trivial unless the 11 | entire category/type is not implemented yet. Please issue a pull request 12 | for any missing APIs and I'll add it! 13 | 14 | ## Installation 15 | 16 | Installation is a little trickier than a standard Go library, but not 17 | by much. You can't simply `go get` this library, unfortunately. This is 18 | because [Z3](https://github.com/Z3Prover/z3) must first be built. We 19 | don't ship a pre-built version of Z3. 20 | 21 | To build Z3, we've made it very easy. You will need the following packages 22 | available on your host operating system: 23 | 24 | * Python 25 | * Make 26 | * gcc/Clang 27 | 28 | Then just type: 29 | 30 | ``` 31 | $ make 32 | ``` 33 | 34 | This will download Z3, compile it, and run the tests for go-z3, 35 | verifying that your build is functional. By default, go-z3 will download 36 | and build the "master" ref of Z3, but this is customizable. 37 | 38 | Compiling/installing the go-z3 library should work on Linux, Mac OS X, 39 | and Windows. On Windows, msys is the only supported build toolchain (same 40 | as Go itself). 41 | 42 | **Due to this linking, it is strongly recommended that you vendor this 43 | repository and bake our build system into your process.** 44 | 45 | ### Customizing the Z3 Compilation 46 | 47 | You can customize the Z3 compilation by setting a couple environmental 48 | variables prior to calling `make`: 49 | 50 | * `Z3_REF` is the git ref that will be checked out for Z3. This 51 | defaults to to a recently tagged version. It is recommended that you 52 | explicitly set this to a ref that works for you to avoid any changes 53 | in this library later. 54 | 55 | ## Usage 56 | 57 | go-z3 exposes the Z3 API in a style that mostly idiomatic Go. The API 58 | should be comfortable to use by any Go programmer without having intimate 59 | knowledge of how Z3 works. 60 | 61 | For usage examples and documentation, please see the 62 | [go-z3 GoDoc](http://godoc.org/github.com/mitchellh/go-z3), which 63 | we keep up to date and full of examples. 64 | 65 | For a quick taste of what using go-z3 looks like, though, we provide 66 | a basic example below: 67 | 68 | ```go 69 | package main 70 | 71 | import ( 72 | "fmt" 73 | 74 | "github.com/mitchellh/go-z3" 75 | ) 76 | 77 | func main() { 78 | // Create the context 79 | config := z3.NewConfig() 80 | ctx := z3.NewContext(config) 81 | config.Close() 82 | defer ctx.Close() 83 | 84 | // Logic: 85 | // x + y + z > 4 86 | // x + y < 2 87 | // z > 0 88 | // x != y != z 89 | // x, y, z != 0 90 | // x + y = -3 91 | 92 | // Create the solver 93 | s := ctx.NewSolver() 94 | defer s.Close() 95 | 96 | // Vars 97 | x := ctx.Const(ctx.Symbol("x"), ctx.IntSort()) 98 | y := ctx.Const(ctx.Symbol("y"), ctx.IntSort()) 99 | z := ctx.Const(ctx.Symbol("z"), ctx.IntSort()) 100 | 101 | zero := ctx.Int(0, ctx.IntSort()) // To save repeats 102 | 103 | // x + y + z > 4 104 | s.Assert(x.Add(y, z).Gt(ctx.Int(4, ctx.IntSort()))) 105 | 106 | // x + y < 2 107 | s.Assert(x.Add(y).Lt(ctx.Int(2, ctx.IntSort()))) 108 | 109 | // z > 0 110 | s.Assert(z.Gt(zero)) 111 | 112 | // x != y != z 113 | s.Assert(x.Distinct(y, z)) 114 | 115 | // x, y, z != 0 116 | s.Assert(x.Eq(zero).Not()) 117 | s.Assert(y.Eq(zero).Not()) 118 | s.Assert(z.Eq(zero).Not()) 119 | 120 | // x + y = -3 121 | s.Assert(x.Add(y).Eq(ctx.Int(-3, ctx.IntSort()))) 122 | 123 | if v := s.Check(); v != True { 124 | fmt.Println("Unsolveable") 125 | return 126 | } 127 | 128 | // Get the resulting model: 129 | m := s.Model() 130 | assignments := m.Assignments() 131 | m.Close() 132 | fmt.Printf("x = %s\n", assignments["x"]) 133 | fmt.Printf("y = %s\n", assignments["y"]) 134 | fmt.Printf("z = %s\n", assignments["z"]) 135 | 136 | // Output: 137 | // x = (- 2) 138 | // y = (- 1) 139 | // z = 8 140 | } 141 | ``` 142 | 143 | ## Issues and Contributing 144 | 145 | If you find an issue with this library, please report an issue. If you'd like, 146 | we welcome any contributions. Fork this library and submit a pull request. 147 | -------------------------------------------------------------------------------- /ast.go: -------------------------------------------------------------------------------- 1 | package z3 2 | 3 | // #include 4 | // #include "go-z3.h" 5 | import "C" 6 | 7 | // AST represents an AST value in Z3. 8 | // 9 | // AST memory management is automatically managed by the Context it 10 | // is contained within. When the Context is freed, so are the AST nodes. 11 | type AST struct { 12 | rawCtx C.Z3_context 13 | rawAST C.Z3_ast 14 | } 15 | 16 | // String returns a human-friendly string version of the AST. 17 | func (a *AST) String() string { 18 | return C.GoString(C.Z3_ast_to_string(a.rawCtx, a.rawAST)) 19 | } 20 | 21 | // DeclName returns the name of a declaration. The AST value must be a 22 | // func declaration for this to work. 23 | func (a *AST) DeclName() *Symbol { 24 | return &Symbol{ 25 | rawCtx: a.rawCtx, 26 | rawSymbol: C.Z3_get_decl_name( 27 | a.rawCtx, C.Z3_to_func_decl(a.rawCtx, a.rawAST)), 28 | } 29 | } 30 | 31 | //------------------------------------------------------------------- 32 | // Var, Literal Creation 33 | //------------------------------------------------------------------- 34 | 35 | // Const declares a variable. It is called "Const" since internally 36 | // this is equivalent to create a function that always returns a constant 37 | // value. From an initial user perspective this may be confusing but go-z3 38 | // is following identical naming convention. 39 | func (c *Context) Const(s *Symbol, typ *Sort) *AST { 40 | return &AST{ 41 | rawCtx: c.raw, 42 | rawAST: C.Z3_mk_const(c.raw, s.rawSymbol, typ.rawSort), 43 | } 44 | } 45 | 46 | // Int creates an integer type. 47 | // 48 | // Maps: Z3_mk_int 49 | func (c *Context) Int(v int, typ *Sort) *AST { 50 | return &AST{ 51 | rawCtx: c.raw, 52 | rawAST: C.Z3_mk_int(c.raw, C.int(v), typ.rawSort), 53 | } 54 | } 55 | 56 | // True creates the value "true". 57 | // 58 | // Maps: Z3_mk_true 59 | func (c *Context) True() *AST { 60 | return &AST{ 61 | rawCtx: c.raw, 62 | rawAST: C.Z3_mk_true(c.raw), 63 | } 64 | } 65 | 66 | // False creates the value "false". 67 | // 68 | // Maps: Z3_mk_false 69 | func (c *Context) False() *AST { 70 | return &AST{ 71 | rawCtx: c.raw, 72 | rawAST: C.Z3_mk_false(c.raw), 73 | } 74 | } 75 | 76 | //------------------------------------------------------------------- 77 | // Value Readers 78 | //------------------------------------------------------------------- 79 | 80 | // Int gets the integer value of this AST. The value must be able to fit 81 | // into a machine integer. 82 | func (a *AST) Int() int { 83 | var dst C.int 84 | C.Z3_get_numeral_int(a.rawCtx, a.rawAST, &dst) 85 | return int(dst) 86 | } 87 | -------------------------------------------------------------------------------- /ast_arith.go: -------------------------------------------------------------------------------- 1 | package z3 2 | 3 | import ( 4 | "unsafe" 5 | ) 6 | 7 | // #include "go-z3.h" 8 | import "C" 9 | 10 | // Add creates an AST node representing adding. 11 | // 12 | // All AST values must be part of the same context. 13 | func (a *AST) Add(args ...*AST) *AST { 14 | raws := make([]C.Z3_ast, len(args)+1) 15 | raws[0] = a.rawAST 16 | for i, arg := range args { 17 | raws[i+1] = arg.rawAST 18 | } 19 | 20 | return &AST{ 21 | rawCtx: a.rawCtx, 22 | rawAST: C.Z3_mk_add( 23 | a.rawCtx, 24 | C.uint(len(raws)), 25 | (*C.Z3_ast)(unsafe.Pointer(&raws[0]))), 26 | } 27 | } 28 | 29 | // Mul creates an AST node representing multiplication. 30 | // 31 | // All AST values must be part of the same context. 32 | func (a *AST) Mul(args ...*AST) *AST { 33 | raws := make([]C.Z3_ast, len(args)+1) 34 | raws[0] = a.rawAST 35 | for i, arg := range args { 36 | raws[i+1] = arg.rawAST 37 | } 38 | 39 | return &AST{ 40 | rawCtx: a.rawCtx, 41 | rawAST: C.Z3_mk_mul( 42 | a.rawCtx, 43 | C.uint(len(raws)), 44 | (*C.Z3_ast)(unsafe.Pointer(&raws[0]))), 45 | } 46 | } 47 | 48 | // Sub creates an AST node representing subtraction. 49 | // 50 | // All AST values must be part of the same context. 51 | func (a *AST) Sub(args ...*AST) *AST { 52 | raws := make([]C.Z3_ast, len(args)+1) 53 | raws[0] = a.rawAST 54 | for i, arg := range args { 55 | raws[i+1] = arg.rawAST 56 | } 57 | 58 | return &AST{ 59 | rawCtx: a.rawCtx, 60 | rawAST: C.Z3_mk_sub( 61 | a.rawCtx, 62 | C.uint(len(raws)), 63 | (*C.Z3_ast)(unsafe.Pointer(&raws[0]))), 64 | } 65 | } 66 | 67 | // Lt creates a "less than" comparison. 68 | // 69 | // Maps to: Z3_mk_lt 70 | func (a *AST) Lt(a2 *AST) *AST { 71 | return &AST{ 72 | rawCtx: a.rawCtx, 73 | rawAST: C.Z3_mk_lt(a.rawCtx, a.rawAST, a2.rawAST), 74 | } 75 | } 76 | 77 | // Le creates a "less than" comparison. 78 | // 79 | // Maps to: Z3_mk_le 80 | func (a *AST) Le(a2 *AST) *AST { 81 | return &AST{ 82 | rawCtx: a.rawCtx, 83 | rawAST: C.Z3_mk_le(a.rawCtx, a.rawAST, a2.rawAST), 84 | } 85 | } 86 | 87 | // Gt creates a "greater than" comparison. 88 | // 89 | // Maps to: Z3_mk_gt 90 | func (a *AST) Gt(a2 *AST) *AST { 91 | return &AST{ 92 | rawCtx: a.rawCtx, 93 | rawAST: C.Z3_mk_gt(a.rawCtx, a.rawAST, a2.rawAST), 94 | } 95 | } 96 | 97 | // Ge creates a "less than" comparison. 98 | // 99 | // Maps to: Z3_mk_ge 100 | func (a *AST) Ge(a2 *AST) *AST { 101 | return &AST{ 102 | rawCtx: a.rawCtx, 103 | rawAST: C.Z3_mk_ge(a.rawCtx, a.rawAST, a2.rawAST), 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /ast_arith_test.go: -------------------------------------------------------------------------------- 1 | package z3 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestASTAdd(t *testing.T) { 8 | config := NewConfig() 9 | defer config.Close() 10 | 11 | ctx := NewContext(config) 12 | defer ctx.Close() 13 | 14 | // Create an int 15 | v1 := ctx.Int(1, ctx.IntSort()) 16 | v2 := ctx.Int(2, ctx.IntSort()) 17 | v3 := ctx.Int(3, ctx.IntSort()) 18 | 19 | // Add 20 | raw := v1.Add(v2, v3) 21 | 22 | actual := raw.String() 23 | if actual != "(+ 1 2 3)" { 24 | t.Fatalf("bad:\n%s", actual) 25 | } 26 | } 27 | 28 | func TestASTMul(t *testing.T) { 29 | config := NewConfig() 30 | defer config.Close() 31 | 32 | ctx := NewContext(config) 33 | defer ctx.Close() 34 | 35 | // Create an int 36 | v1 := ctx.Int(1, ctx.IntSort()) 37 | v2 := ctx.Int(2, ctx.IntSort()) 38 | v3 := ctx.Int(3, ctx.IntSort()) 39 | 40 | // Mul 41 | raw := v1.Mul(v2, v3) 42 | 43 | actual := raw.String() 44 | if actual != "(* 1 2 3)" { 45 | t.Fatalf("bad:\n%s", actual) 46 | } 47 | } 48 | 49 | func TestASTSub(t *testing.T) { 50 | config := NewConfig() 51 | defer config.Close() 52 | 53 | ctx := NewContext(config) 54 | defer ctx.Close() 55 | 56 | // Create an int 57 | v1 := ctx.Int(1, ctx.IntSort()) 58 | v2 := ctx.Int(2, ctx.IntSort()) 59 | v3 := ctx.Int(3, ctx.IntSort()) 60 | 61 | // Sub 62 | raw := v1.Sub(v2, v3) 63 | 64 | actual := raw.String() 65 | if actual != "(- (- 1 2) 3)" { 66 | t.Fatalf("bad:\n%s", actual) 67 | } 68 | } 69 | 70 | func TestASTLt(t *testing.T) { 71 | config := NewConfig() 72 | defer config.Close() 73 | ctx := NewContext(config) 74 | defer ctx.Close() 75 | 76 | // Create an int 77 | x := ctx.Const(ctx.Symbol("x"), ctx.IntSort()) 78 | y := ctx.Const(ctx.Symbol("y"), ctx.IntSort()) 79 | 80 | // Add 81 | raw := x.Lt(y) 82 | 83 | actual := raw.String() 84 | if actual != "(< x y)" { 85 | t.Fatalf("bad:\n%s", actual) 86 | } 87 | } 88 | 89 | func TestASTLe(t *testing.T) { 90 | config := NewConfig() 91 | defer config.Close() 92 | ctx := NewContext(config) 93 | defer ctx.Close() 94 | 95 | // Create an int 96 | x := ctx.Const(ctx.Symbol("x"), ctx.IntSort()) 97 | y := ctx.Const(ctx.Symbol("y"), ctx.IntSort()) 98 | 99 | // Add 100 | raw := x.Le(y) 101 | 102 | actual := raw.String() 103 | if actual != "(<= x y)" { 104 | t.Fatalf("bad:\n%s", actual) 105 | } 106 | } 107 | 108 | func TestASTGt(t *testing.T) { 109 | config := NewConfig() 110 | defer config.Close() 111 | ctx := NewContext(config) 112 | defer ctx.Close() 113 | 114 | // Create an int 115 | x := ctx.Const(ctx.Symbol("x"), ctx.IntSort()) 116 | y := ctx.Const(ctx.Symbol("y"), ctx.IntSort()) 117 | 118 | // Add 119 | raw := x.Gt(y) 120 | 121 | actual := raw.String() 122 | if actual != "(> x y)" { 123 | t.Fatalf("bad:\n%s", actual) 124 | } 125 | } 126 | 127 | func TestASTGe(t *testing.T) { 128 | config := NewConfig() 129 | defer config.Close() 130 | ctx := NewContext(config) 131 | defer ctx.Close() 132 | 133 | // Create an int 134 | x := ctx.Const(ctx.Symbol("x"), ctx.IntSort()) 135 | y := ctx.Const(ctx.Symbol("y"), ctx.IntSort()) 136 | 137 | // Add 138 | raw := x.Ge(y) 139 | 140 | actual := raw.String() 141 | if actual != "(>= x y)" { 142 | t.Fatalf("bad:\n%s", actual) 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /ast_logic.go: -------------------------------------------------------------------------------- 1 | package z3 2 | 3 | import ( 4 | "unsafe" 5 | ) 6 | 7 | // #include "go-z3.h" 8 | import "C" 9 | 10 | // Distinct creates an AST node representing adding. 11 | // 12 | // All AST values must be part of the same context. 13 | func (a *AST) Distinct(args ...*AST) *AST { 14 | raws := make([]C.Z3_ast, len(args)+1) 15 | raws[0] = a.rawAST 16 | for i, arg := range args { 17 | raws[i+1] = arg.rawAST 18 | } 19 | 20 | return &AST{ 21 | rawCtx: a.rawCtx, 22 | rawAST: C.Z3_mk_distinct( 23 | a.rawCtx, 24 | C.uint(len(raws)), 25 | (*C.Z3_ast)(unsafe.Pointer(&raws[0]))), 26 | } 27 | } 28 | 29 | // Not creates an AST node representing not(a) 30 | // 31 | // Maps to: Z3_mk_not 32 | func (a *AST) Not() *AST { 33 | return &AST{ 34 | rawCtx: a.rawCtx, 35 | rawAST: C.Z3_mk_not(a.rawCtx, a.rawAST), 36 | } 37 | } 38 | 39 | // Eq creates a "equal" comparison. 40 | // 41 | // Maps to: Z3_mk_eq 42 | func (a *AST) Eq(a2 *AST) *AST { 43 | return &AST{ 44 | rawCtx: a.rawCtx, 45 | rawAST: C.Z3_mk_eq(a.rawCtx, a.rawAST, a2.rawAST), 46 | } 47 | } 48 | 49 | // Ite creates an AST node representing if a then a2 else a3. 50 | // 51 | // a and a2 must be part of the same Context and be boolean types. 52 | func (a *AST) Ite(a2, a3 *AST) *AST { 53 | return &AST{ 54 | rawCtx: a.rawCtx, 55 | rawAST: C.Z3_mk_ite(a.rawCtx, a.rawAST, a2.rawAST, a3.rawAST), 56 | } 57 | } 58 | 59 | // Iff creates an AST node representing a iff a2. 60 | // 61 | // a and a2 must be part of the same Context and be boolean types. 62 | func (a *AST) Iff(a2 *AST) *AST { 63 | return &AST{ 64 | rawCtx: a.rawCtx, 65 | rawAST: C.Z3_mk_iff(a.rawCtx, a.rawAST, a2.rawAST), 66 | } 67 | } 68 | 69 | // Implies creates an AST node representing a implies a2. 70 | // 71 | // a and a2 must be part of the same Context and be boolean types. 72 | func (a *AST) Implies(a2 *AST) *AST { 73 | return &AST{ 74 | rawCtx: a.rawCtx, 75 | rawAST: C.Z3_mk_implies(a.rawCtx, a.rawAST, a2.rawAST), 76 | } 77 | } 78 | 79 | // Xor creates an AST node representing a xor a2. 80 | // 81 | // a and a2 must be part of the same Context and be boolean types. 82 | func (a *AST) Xor(a2 *AST) *AST { 83 | return &AST{ 84 | rawCtx: a.rawCtx, 85 | rawAST: C.Z3_mk_xor(a.rawCtx, a.rawAST, a2.rawAST), 86 | } 87 | } 88 | 89 | // And creates an AST node representing a and a2 and ... aN. 90 | // 91 | // a and a2 must be part of the same Context and be boolean types. 92 | func (a *AST) And(args ...*AST) *AST { 93 | raws := make([]C.Z3_ast, len(args)+1) 94 | raws[0] = a.rawAST 95 | for i, arg := range args { 96 | raws[i+1] = arg.rawAST 97 | } 98 | 99 | return &AST{ 100 | rawCtx: a.rawCtx, 101 | rawAST: C.Z3_mk_and( 102 | a.rawCtx, 103 | C.uint(len(raws)), 104 | (*C.Z3_ast)(unsafe.Pointer(&raws[0]))), 105 | } 106 | } 107 | 108 | // Or creates an AST node representing a or a2 or ... aN. 109 | // 110 | // a and a2 must be part of the same Context and be boolean types. 111 | func (a *AST) Or(args ...*AST) *AST { 112 | raws := make([]C.Z3_ast, len(args)+1) 113 | raws[0] = a.rawAST 114 | for i, arg := range args { 115 | raws[i+1] = arg.rawAST 116 | } 117 | 118 | return &AST{ 119 | rawCtx: a.rawCtx, 120 | rawAST: C.Z3_mk_or( 121 | a.rawCtx, 122 | C.uint(len(raws)), 123 | (*C.Z3_ast)(unsafe.Pointer(&raws[0]))), 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /ast_logic_test.go: -------------------------------------------------------------------------------- 1 | package z3 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestASTDistinct(t *testing.T) { 8 | config := NewConfig() 9 | defer config.Close() 10 | ctx := NewContext(config) 11 | defer ctx.Close() 12 | 13 | // Create an int 14 | x := ctx.Const(ctx.Symbol("x"), ctx.IntSort()) 15 | y := ctx.Const(ctx.Symbol("y"), ctx.IntSort()) 16 | z := ctx.Const(ctx.Symbol("z"), ctx.IntSort()) 17 | 18 | // Add 19 | raw := x.Distinct(y, z) 20 | 21 | actual := raw.String() 22 | if actual != "(distinct x y z)" { 23 | t.Fatalf("bad:\n%s", actual) 24 | } 25 | } 26 | 27 | func TestASTNot(t *testing.T) { 28 | config := NewConfig() 29 | defer config.Close() 30 | ctx := NewContext(config) 31 | defer ctx.Close() 32 | 33 | // Create an int 34 | x := ctx.Const(ctx.Symbol("x"), ctx.BoolSort()) 35 | 36 | // Add 37 | raw := x.Not() 38 | 39 | actual := raw.String() 40 | if actual != "(not x)" { 41 | t.Fatalf("bad:\n%s", actual) 42 | } 43 | } 44 | 45 | func TestASTEq(t *testing.T) { 46 | config := NewConfig() 47 | defer config.Close() 48 | ctx := NewContext(config) 49 | defer ctx.Close() 50 | 51 | // Create an int 52 | x := ctx.Const(ctx.Symbol("x"), ctx.BoolSort()) 53 | y := ctx.Const(ctx.Symbol("y"), ctx.BoolSort()) 54 | 55 | // Add 56 | raw := x.Eq(y) 57 | 58 | actual := raw.String() 59 | if actual != "(= x y)" { 60 | t.Fatalf("bad:\n%s", actual) 61 | } 62 | } 63 | 64 | func TestASTIte(t *testing.T) { 65 | config := NewConfig() 66 | defer config.Close() 67 | ctx := NewContext(config) 68 | defer ctx.Close() 69 | 70 | // Create an int 71 | x := ctx.Const(ctx.Symbol("x"), ctx.BoolSort()) 72 | y := ctx.Const(ctx.Symbol("y"), ctx.BoolSort()) 73 | z := ctx.Const(ctx.Symbol("z"), ctx.BoolSort()) 74 | 75 | raw := x.Ite(y, z) 76 | 77 | actual := raw.String() 78 | if actual != "(ite x y z)" { 79 | t.Fatalf("bad:\n%s", actual) 80 | } 81 | } 82 | 83 | func TestASTIff(t *testing.T) { 84 | config := NewConfig() 85 | defer config.Close() 86 | ctx := NewContext(config) 87 | defer ctx.Close() 88 | 89 | // Create an int 90 | x := ctx.Const(ctx.Symbol("x"), ctx.BoolSort()) 91 | y := ctx.Const(ctx.Symbol("y"), ctx.BoolSort()) 92 | 93 | raw := x.Iff(y) 94 | 95 | actual := raw.String() 96 | if actual != "(= x y)" { 97 | t.Fatalf("bad:\n%s", actual) 98 | } 99 | } 100 | 101 | func TestASTImplies(t *testing.T) { 102 | config := NewConfig() 103 | defer config.Close() 104 | ctx := NewContext(config) 105 | defer ctx.Close() 106 | 107 | // Create an int 108 | x := ctx.Const(ctx.Symbol("x"), ctx.BoolSort()) 109 | y := ctx.Const(ctx.Symbol("y"), ctx.BoolSort()) 110 | 111 | // Add 112 | raw := x.Implies(y) 113 | 114 | actual := raw.String() 115 | if actual != "(=> x y)" { 116 | t.Fatalf("bad:\n%s", actual) 117 | } 118 | } 119 | 120 | func TestASTXor(t *testing.T) { 121 | config := NewConfig() 122 | defer config.Close() 123 | ctx := NewContext(config) 124 | defer ctx.Close() 125 | 126 | // Create an int 127 | x := ctx.Const(ctx.Symbol("x"), ctx.BoolSort()) 128 | y := ctx.Const(ctx.Symbol("y"), ctx.BoolSort()) 129 | 130 | // Add 131 | raw := x.Xor(y) 132 | 133 | actual := raw.String() 134 | if actual != "(xor x y)" { 135 | t.Fatalf("bad:\n%s", actual) 136 | } 137 | } 138 | 139 | func TestASTAnd(t *testing.T) { 140 | config := NewConfig() 141 | defer config.Close() 142 | ctx := NewContext(config) 143 | defer ctx.Close() 144 | 145 | // Create an int 146 | x := ctx.Const(ctx.Symbol("x"), ctx.BoolSort()) 147 | y := ctx.Const(ctx.Symbol("y"), ctx.BoolSort()) 148 | 149 | // Add 150 | raw := x.And(y) 151 | 152 | actual := raw.String() 153 | if actual != "(and x y)" { 154 | t.Fatalf("bad:\n%s", actual) 155 | } 156 | } 157 | 158 | func TestASTOr(t *testing.T) { 159 | config := NewConfig() 160 | defer config.Close() 161 | ctx := NewContext(config) 162 | defer ctx.Close() 163 | 164 | // Create an int 165 | x := ctx.Const(ctx.Symbol("x"), ctx.BoolSort()) 166 | y := ctx.Const(ctx.Symbol("y"), ctx.BoolSort()) 167 | 168 | // Add 169 | raw := x.Or(y) 170 | 171 | actual := raw.String() 172 | if actual != "(or x y)" { 173 | t.Fatalf("bad:\n%s", actual) 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /config.go: -------------------------------------------------------------------------------- 1 | package z3 2 | 3 | import ( 4 | "unsafe" 5 | ) 6 | 7 | // #include 8 | // #include "go-z3.h" 9 | import "C" 10 | 11 | // Config is used to set configuration for Z3. This should be created with 12 | // NewConfig and closed with Close when you're done using it. 13 | // 14 | // Config structures are used to set parameters for Z3 behavior. See the 15 | // Z3 docs for information on available parameters. They can be set with 16 | // SetParamValue. 17 | // 18 | // As for 2016-03-02, the parameters available are documented as: 19 | // 20 | // proof (Boolean) Enable proof generation 21 | // debug_ref_count (Boolean) Enable debug support for Z3_ast reference counting 22 | // trace (Boolean) Tracing support for VCC 23 | // trace_file_name (String) Trace out file for VCC traces 24 | // timeout (unsigned) default timeout (in milliseconds) used for solvers 25 | // well_sorted_check type checker 26 | // auto_config use heuristics to automatically select solver and configure it 27 | // model model generation for solvers, this parameter can be overwritten when creating a solver 28 | // model_validate validate models produced by solvers 29 | // unsat_core unsat-core generation for solvers, this parameter can be overwritten when creating a solver 30 | // 31 | type Config struct { 32 | raw C.Z3_config 33 | } 34 | 35 | // NewConfig allocates a new configuration object. 36 | func NewConfig() *Config { 37 | return &Config{ 38 | raw: C.Z3_mk_config(), 39 | } 40 | } 41 | 42 | // Close frees the memory associated with this configuration 43 | func (c *Config) Close() error { 44 | C.Z3_del_config(c.raw) 45 | return nil 46 | } 47 | 48 | // SetParamValue sets the parameters for a Config. See the Config docs. 49 | func (c *Config) SetParamValue(k, v string) { 50 | ck := C.CString(k) 51 | cv := C.CString(v) 52 | 53 | // We free the strings since they're not actually stored 54 | defer C.free(unsafe.Pointer(ck)) 55 | defer C.free(unsafe.Pointer(cv)) 56 | 57 | C.Z3_set_param_value(c.raw, ck, cv) 58 | } 59 | 60 | // Z3Value returns the raw internal pointer value. This should only be 61 | // used if you really understand what you're doing. It may be invalid after 62 | // Close is called. 63 | func (c *Config) Z3Value() C.Z3_config { 64 | return c.raw 65 | } 66 | -------------------------------------------------------------------------------- /config_test.go: -------------------------------------------------------------------------------- 1 | package z3 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestConfig(t *testing.T) { 8 | c := NewConfig() 9 | c.SetParamValue("proof", "true") 10 | c.Close() 11 | } 12 | -------------------------------------------------------------------------------- /context.go: -------------------------------------------------------------------------------- 1 | package z3 2 | 3 | // #include "go-z3.h" 4 | import "C" 5 | 6 | // Context is what handles most of the interactions with Z3. 7 | type Context struct { 8 | raw C.Z3_context 9 | } 10 | 11 | func NewContext(c *Config) *Context { 12 | return &Context{ 13 | raw: C.Z3_mk_context(c.Z3Value()), 14 | } 15 | } 16 | 17 | // Close frees the memory associated with this context. 18 | func (c *Context) Close() error { 19 | // Clear context 20 | C.Z3_del_context(c.raw) 21 | 22 | // Clear error handling 23 | errorHandlerMapLock.Lock() 24 | delete(errorHandlerMap, c.raw) 25 | errorHandlerMapLock.Unlock() 26 | 27 | return nil 28 | } 29 | 30 | // Z3Value returns the internal structure for this Context. 31 | func (c *Context) Z3Value() C.Z3_context { 32 | return c.raw 33 | } 34 | -------------------------------------------------------------------------------- /context_test.go: -------------------------------------------------------------------------------- 1 | package z3 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestContext(t *testing.T) { 8 | config := NewConfig() 9 | defer config.Close() 10 | 11 | ctx := NewContext(config) 12 | ctx.Close() 13 | } 14 | -------------------------------------------------------------------------------- /enum.go: -------------------------------------------------------------------------------- 1 | package z3 2 | 3 | // #include "go-z3.h" 4 | import "C" 5 | 6 | // LBool is the lifted boolean type representing false, true, and undefined. 7 | type LBool int8 8 | 9 | const ( 10 | False LBool = C.Z3_L_FALSE 11 | Undef = C.Z3_L_UNDEF 12 | True = C.Z3_L_TRUE 13 | ) 14 | -------------------------------------------------------------------------------- /error.go: -------------------------------------------------------------------------------- 1 | package z3 2 | 3 | import "sync" 4 | 5 | // #include "go-z3.h" 6 | import "C" 7 | 8 | // ErrorCode represents the enum of error codes Z3 supports. 9 | type ErrorCode uint 10 | 11 | const ( 12 | ErrorCodeOk ErrorCode = C.Z3_OK 13 | ErrorCodeSortError = C.Z3_SORT_ERROR 14 | ErrorCodeIOB = C.Z3_IOB 15 | ErrorCodeInvalidArg = C.Z3_INVALID_ARG 16 | ErrorCodeParserError = C.Z3_PARSER_ERROR 17 | ErrorCodeNoParser = C.Z3_NO_PARSER 18 | ErrorCodeInvalidPattern = C.Z3_INVALID_PATTERN 19 | ErrorCodeMemoutFail = C.Z3_MEMOUT_FAIL 20 | ErrorCodeFileAccessError = C.Z3_FILE_ACCESS_ERROR 21 | ErrorCodeInternalFatal = C.Z3_INTERNAL_FATAL 22 | ErrorCodeInvalidUsage = C.Z3_INVALID_USAGE 23 | ErrorCodeDecRefError = C.Z3_DEC_REF_ERROR 24 | ErrorCodeException = C.Z3_EXCEPTION 25 | ) 26 | 27 | // ErrorHandler is the callback that is invoked when an error occurs in 28 | // Z3 and is registered by SetErrorHandler. 29 | type ErrorHandler func(*Context, ErrorCode) 30 | 31 | // These unexported vars are used to keep track of our error handlers. 32 | var errorHandlerMap = map[C.Z3_context]ErrorHandler{} 33 | var errorHandlerMapLock sync.RWMutex 34 | 35 | // SetErrorHandler registers the error handler. This handler is invoked 36 | // whenever an error occurs within Z3. 37 | func (c *Context) SetErrorHandler(f ErrorHandler) { 38 | C.Z3_set_error_handler(c.raw, C._go_z3_error_handler()) 39 | 40 | errorHandlerMapLock.Lock() 41 | defer errorHandlerMapLock.Unlock() 42 | errorHandlerMap[c.raw] = f 43 | } 44 | 45 | // Error returns the error message for the given error code. 46 | // This code can be retrieved via the error handler callback. 47 | // 48 | // This MUST be called during the handler. This must not be called later 49 | // since the error state on the context may have cleared. 50 | // 51 | // Maps: Z3_get_error_msg_ex 52 | func (c *Context) Error(code ErrorCode) string { 53 | return C.GoString(C.Z3_get_error_msg(c.raw, C.Z3_error_code(code))) 54 | } 55 | 56 | //export goZ3ErrorHandler 57 | func goZ3ErrorHandler(raw C.Z3_context, code C.Z3_error_code) { 58 | errorHandlerMapLock.RLock() 59 | defer errorHandlerMapLock.RUnlock() 60 | 61 | // Look up the error handler for this context 62 | f, ok := errorHandlerMap[raw] 63 | if !ok { 64 | return 65 | } 66 | 67 | // Call it! 68 | f(&Context{raw: raw}, ErrorCode(code)) 69 | } 70 | -------------------------------------------------------------------------------- /error_test.go: -------------------------------------------------------------------------------- 1 | package z3 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | ) 7 | 8 | func TestContextErrorHandler(t *testing.T) { 9 | config := NewConfig() 10 | defer config.Close() 11 | ctx := NewContext(config) 12 | defer ctx.Close() 13 | 14 | // Set an error handler 15 | called := false 16 | msg := "" 17 | ctx.SetErrorHandler(func(c *Context, code ErrorCode) { 18 | called = true 19 | msg = c.Error(code) 20 | }) 21 | 22 | // Create an int 23 | x := ctx.Const(ctx.Symbol("x"), ctx.BoolSort()) 24 | y := ctx.Const(ctx.Symbol("y"), ctx.BoolSort()) 25 | 26 | // This won't work because x and y aren't ints 27 | x.Ge(y) 28 | if !called { 29 | t.Fatal("should call error handler") 30 | } 31 | if !strings.Contains(msg, "Sort mismatch") { 32 | t.Fatalf("bad: %s", msg) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /go-z3.h: -------------------------------------------------------------------------------- 1 | // vim: ft=c ts=2 sts=2 st=2 2 | /* 3 | * This header exists to simplify the headers that are included within 4 | * the Go files. This header should include all the necessary headers 5 | * for the compilation of the Go library. 6 | * */ 7 | 8 | #ifndef _GOZ3_H_INCLUDED 9 | #define _GOZ3_H_INCLUDED 10 | 11 | #include 12 | 13 | //------------------------------------------------------------------- 14 | // Error handling helpers 15 | //------------------------------------------------------------------- 16 | // This is declared in error.go and is a way for us to call back into 17 | // Go to execute the proper error handlers. 18 | extern void goZ3ErrorHandler(Z3_context, Z3_error_code); 19 | 20 | // This method is used as a way to get a valid error handler 21 | // pointer back into Go. 22 | static inline Z3_error_handler* _go_z3_error_handler() { 23 | return &goZ3ErrorHandler; 24 | } 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /model.go: -------------------------------------------------------------------------------- 1 | package z3 2 | 3 | // #include "go-z3.h" 4 | /* 5 | int _Z3_model_eval(Z3_context c, Z3_model m, Z3_ast t, int model_completion, Z3_ast * v) { 6 | return Z3_model_eval(c, m, t, (bool) model_completion, v); 7 | } 8 | 9 | */ 10 | import "C" 11 | 12 | // Model represents a model from a solver. 13 | // 14 | // Memory management for this is manual and based on reference counting. 15 | // When a model is initialized (via Solver.Model for example), it always has 16 | // a reference count of 1. You must call Close when you're done. 17 | type Model struct { 18 | rawCtx C.Z3_context 19 | rawModel C.Z3_model 20 | } 21 | 22 | // String returns a human-friendly string version of the model. 23 | func (m *Model) String() string { 24 | return C.GoString(C.Z3_model_to_string(m.rawCtx, m.rawModel)) 25 | } 26 | 27 | //------------------------------------------------------------------- 28 | // Assignments 29 | //------------------------------------------------------------------- 30 | 31 | // Eval evaluates the given AST within the model. This can be used to get 32 | // the assignment of an AST. This will return nil if evaluation failed. 33 | // 34 | // For example: 35 | // 36 | // x := ctx.Const(ctx.Symbol("x"), ctx.IntSort()) 37 | // // ... further solving 38 | // m.Eval(x) => x's value 39 | // 40 | // Maps: Z3_model_eval 41 | func (m *Model) Eval(c *AST) *AST { 42 | var result C.Z3_ast 43 | if C._Z3_model_eval(m.rawCtx, m.rawModel, c.rawAST, 1, &result) == 0 { 44 | return nil 45 | } 46 | 47 | return &AST{ 48 | rawCtx: m.rawCtx, 49 | rawAST: result, 50 | } 51 | } 52 | 53 | // Assignments returns a map of all the assignments for all the constants 54 | // within the model. The key of the map will be the String value of the 55 | // symbol. 56 | // 57 | // This doesn't map to any specific Z3 API. This is a higher-level function 58 | // provided by go-z3 to make the Z3 API easier to consume in Go. 59 | func (m *Model) Assignments() map[string]*AST { 60 | result := make(map[string]*AST) 61 | for i := uint(0); i < m.NumConsts(); i++ { 62 | // Get the declaration 63 | decl := m.ConstDecl(i) 64 | 65 | // Get the name of it, i.e. "x" 66 | name := decl.DeclName() 67 | 68 | // Get the assignment for this 69 | ast := C.Z3_model_get_const_interp( 70 | m.rawCtx, m.rawModel, C.Z3_to_func_decl(decl.rawCtx, decl.rawAST)) 71 | 72 | // Map it 73 | result[name.String()] = &AST{ 74 | rawCtx: m.rawCtx, 75 | rawAST: ast, 76 | } 77 | } 78 | 79 | return result 80 | } 81 | 82 | // NumConsts returns the number of constant assignments. 83 | // 84 | // Maps: Z3_model_get_num_consts 85 | func (m *Model) NumConsts() uint { 86 | return uint(C.Z3_model_get_num_consts(m.rawCtx, m.rawModel)) 87 | } 88 | 89 | // ConstDecl returns the const declaration for the given index. idx must 90 | // be less than NumConsts. 91 | // 92 | // Maps: Z3_model_get_const_decl 93 | func (m *Model) ConstDecl(idx uint) *AST { 94 | return &AST{ 95 | rawCtx: m.rawCtx, 96 | rawAST: C.Z3_func_decl_to_ast( 97 | m.rawCtx, 98 | C.Z3_model_get_const_decl(m.rawCtx, m.rawModel, C.uint(idx))), 99 | } 100 | } 101 | 102 | //------------------------------------------------------------------- 103 | // Memory Management 104 | //------------------------------------------------------------------- 105 | 106 | // Close decreases the reference count for this model. If nothing else 107 | // has manually increased the reference count, this will free the memory 108 | // associated with it. 109 | func (m *Model) Close() error { 110 | C.Z3_model_dec_ref(m.rawCtx, m.rawModel) 111 | return nil 112 | } 113 | 114 | // IncRef increases the reference count of this model. This is advanced, 115 | // you probably don't need to use this. 116 | func (m *Model) IncRef() { 117 | C.Z3_model_inc_ref(m.rawCtx, m.rawModel) 118 | } 119 | 120 | // DecRef decreases the reference count of this model. This is advanced, 121 | // you probably don't need to use this. 122 | // 123 | // Close will decrease it automatically from the initial 1, so this should 124 | // only be called with exact matching calls to IncRef. 125 | func (m *Model) DecRef() { 126 | C.Z3_model_dec_ref(m.rawCtx, m.rawModel) 127 | } 128 | -------------------------------------------------------------------------------- /model_test.go: -------------------------------------------------------------------------------- 1 | package z3 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestModelAssignments(t *testing.T) { 8 | config := NewConfig() 9 | defer config.Close() 10 | 11 | ctx := NewContext(config) 12 | defer ctx.Close() 13 | 14 | // Create a symbol 15 | x := ctx.Const(ctx.Symbol("x"), ctx.IntSort()) 16 | 17 | // x + 4 = 16 18 | ast := x.Add(ctx.Int(4, ctx.IntSort())). 19 | Eq(ctx.Int(16, ctx.IntSort())) 20 | t.Logf("\nAST:\n%s", ast.String()) 21 | 22 | // Create the solver 23 | s := ctx.NewSolver() 24 | defer s.Close() 25 | 26 | // Assert constraints 27 | s.Assert(ast) 28 | 29 | // Solve 30 | result := s.Check() 31 | if result != True { 32 | t.Fatalf("bad: %v", result) 33 | } 34 | 35 | // Get the model 36 | m := s.Model() 37 | defer m.Close() 38 | t.Logf("\nModel:\n%s", m.String()) 39 | 40 | // Get the exact value 41 | am := m.Assignments() 42 | assign := am["x"] 43 | t.Logf("Assignment: %s", assign) 44 | if assign.Int() != 12 { 45 | t.Fatalf("bad: %s", assign) 46 | } 47 | } 48 | 49 | func TestModelEval(t *testing.T) { 50 | config := NewConfig() 51 | defer config.Close() 52 | 53 | ctx := NewContext(config) 54 | defer ctx.Close() 55 | 56 | // Create a symbol 57 | x := ctx.Const(ctx.Symbol("x"), ctx.IntSort()) 58 | 59 | // x + 4 = 16 60 | ast := x.Add(ctx.Int(4, ctx.IntSort())). 61 | Eq(ctx.Int(16, ctx.IntSort())) 62 | t.Logf("\nAST:\n%s", ast.String()) 63 | 64 | // Create the solver 65 | s := ctx.NewSolver() 66 | defer s.Close() 67 | 68 | // Assert constraints 69 | s.Assert(ast) 70 | 71 | // Solve 72 | result := s.Check() 73 | if result != True { 74 | t.Fatalf("bad: %v", result) 75 | } 76 | 77 | // Get the model 78 | m := s.Model() 79 | defer m.Close() 80 | t.Logf("\nModel:\n%s", m.String()) 81 | 82 | // Get the exact value 83 | assign := m.Eval(x) 84 | t.Logf("Assignment: %s", assign) 85 | if assign.Int() != 12 { 86 | t.Fatalf("bad: %s", assign) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /solver.go: -------------------------------------------------------------------------------- 1 | package z3 2 | 3 | // #include "go-z3.h" 4 | import "C" 5 | 6 | // Solver is a single solver tied to a specific Context within Z3. 7 | // 8 | // It is created via the NewSolver methods on Context. When a solver is 9 | // no longer needed, the Close method must be called. This will remove the 10 | // solver from the context and no more APIs on Solver may be called 11 | // thereafter. 12 | // 13 | // Freeing the context (Context.Close) will NOT automatically close associated 14 | // solvers. They must be managed separately. 15 | type Solver struct { 16 | rawCtx C.Z3_context 17 | rawSolver C.Z3_solver 18 | } 19 | 20 | // NewSolver creates a new solver. 21 | func (c *Context) NewSolver() *Solver { 22 | rawSolver := C.Z3_mk_solver(c.raw) 23 | C.Z3_solver_inc_ref(c.raw, rawSolver) 24 | 25 | return &Solver{ 26 | rawSolver: rawSolver, 27 | rawCtx: c.raw, 28 | } 29 | } 30 | 31 | // Close frees the memory associated with this. 32 | func (s *Solver) Close() error { 33 | C.Z3_solver_dec_ref(s.rawCtx, s.rawSolver) 34 | return nil 35 | } 36 | 37 | // Assert asserts a constraint onto the Solver. 38 | // 39 | // Maps to: Z3_solver_assert 40 | func (s *Solver) Assert(a *AST) { 41 | C.Z3_solver_assert(s.rawCtx, s.rawSolver, a.rawAST) 42 | } 43 | 44 | // Check checks if the currently set formula is consistent. 45 | // 46 | // Maps to: Z3_solver_check 47 | func (s *Solver) Check() LBool { 48 | return LBool(C.Z3_solver_check(s.rawCtx, s.rawSolver)) 49 | } 50 | 51 | // Model returns the last model from a Check. 52 | // 53 | // Maps to: Z3_solver_get_model 54 | func (s *Solver) Model() *Model { 55 | m := &Model{ 56 | rawCtx: s.rawCtx, 57 | rawModel: C.Z3_solver_get_model(s.rawCtx, s.rawSolver), 58 | } 59 | m.IncRef() 60 | return m 61 | } 62 | -------------------------------------------------------------------------------- /solver_test.go: -------------------------------------------------------------------------------- 1 | package z3 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestSolver(t *testing.T) { 8 | config := NewConfig() 9 | defer config.Close() 10 | 11 | ctx := NewContext(config) 12 | defer ctx.Close() 13 | 14 | // Create the "x xor y" constraint 15 | boolTyp := ctx.BoolSort() 16 | x := ctx.Const(ctx.Symbol("x"), boolTyp) 17 | y := ctx.Const(ctx.Symbol("y"), boolTyp) 18 | x_xor_y := x.Xor(y) 19 | ast := x_xor_y 20 | t.Logf("\nAST:\n%s", ast.String()) 21 | 22 | // Create the solver 23 | s := ctx.NewSolver() 24 | defer s.Close() 25 | 26 | // Assert constraints 27 | s.Assert(x_xor_y) 28 | 29 | // Solve 30 | result := s.Check() 31 | if result != True { 32 | t.Fatalf("bad: %v", result) 33 | } 34 | 35 | // Get the model 36 | m := s.Model() 37 | defer m.Close() 38 | t.Logf("\nModel:\n%s", m.String()) 39 | } 40 | -------------------------------------------------------------------------------- /sort.go: -------------------------------------------------------------------------------- 1 | package z3 2 | 3 | // #include "go-z3.h" 4 | import "C" 5 | 6 | // Sort represents a sort in Z3. 7 | type Sort struct { 8 | rawCtx C.Z3_context 9 | rawSort C.Z3_sort 10 | } 11 | 12 | // BoolSort returns the boolean type. 13 | func (c *Context) BoolSort() *Sort { 14 | return &Sort{ 15 | rawCtx: c.raw, 16 | rawSort: C.Z3_mk_bool_sort(c.raw), 17 | } 18 | } 19 | 20 | // IntSort returns the int type. 21 | func (c *Context) IntSort() *Sort { 22 | return &Sort{ 23 | rawCtx: c.raw, 24 | rawSort: C.Z3_mk_int_sort(c.raw), 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /symbol.go: -------------------------------------------------------------------------------- 1 | package z3 2 | 3 | import ( 4 | "strconv" 5 | "unsafe" 6 | ) 7 | 8 | // #include 9 | // #include "go-z3.h" 10 | import "C" 11 | 12 | // Symbol represents a named 13 | type Symbol struct { 14 | rawCtx C.Z3_context 15 | rawSymbol C.Z3_symbol 16 | } 17 | 18 | // Create a symbol named by a string within the context. 19 | // 20 | // The memory associated with this symbol is freed when the context is freed. 21 | func (c *Context) Symbol(name string) *Symbol { 22 | ns := C.CString(name) 23 | defer C.free(unsafe.Pointer(ns)) 24 | 25 | return &Symbol{ 26 | rawCtx: c.raw, 27 | rawSymbol: C.Z3_mk_string_symbol(c.raw, ns), 28 | } 29 | } 30 | 31 | // Create a symbol named by an int within the context. 32 | // 33 | // The memory associated with this symbol is freed when the context is freed. 34 | func (c *Context) SymbolInt(name int) *Symbol { 35 | return &Symbol{ 36 | rawCtx: c.raw, 37 | rawSymbol: C.Z3_mk_int_symbol(c.raw, C.int(name)), 38 | } 39 | } 40 | 41 | // String returns a string value for this symbol no matter what kind 42 | // of symbol it is. If it is an int, it will be converted to a string 43 | // result. 44 | func (s *Symbol) String() string { 45 | switch C.Z3_get_symbol_kind(s.rawCtx, s.rawSymbol) { 46 | case C.Z3_INT_SYMBOL: 47 | return strconv.FormatInt( 48 | int64(C.Z3_get_symbol_int(s.rawCtx, s.rawSymbol)), 10) 49 | 50 | case C.Z3_STRING_SYMBOL: 51 | // We don't need to free this value since it uses statically allocated 52 | // space that is reused by Z3. The GoString call will copy the memory. 53 | return C.GoString(C.Z3_get_symbol_string(s.rawCtx, s.rawSymbol)) 54 | 55 | default: 56 | return "unknown symbol kind" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /symbol_test.go: -------------------------------------------------------------------------------- 1 | package z3 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestSymbol(t *testing.T) { 8 | config := NewConfig() 9 | defer config.Close() 10 | 11 | ctx := NewContext(config) 12 | defer ctx.Close() 13 | 14 | // String symbol 15 | x := ctx.Symbol("x") 16 | if v := x.String(); v != "x" { 17 | t.Fatalf("bad: %q", v) 18 | } 19 | 20 | // Int symbol 21 | y := ctx.SymbolInt(42) 22 | if v := y.String(); v != "42" { 23 | t.Fatalf("bad: %q", v) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /z3.go: -------------------------------------------------------------------------------- 1 | // Package z3 provides Go bindings to the Z3 SMT Solver. 2 | // 3 | // The bindings are a balance between idiomatic Go and being an obvious 4 | // translation from the Z3 API so you can look up Z3 APIs and find the 5 | // intuitive mapping in Go. 6 | // 7 | // The most foreign thing to Go programmers will be error handling. Rather 8 | // than return the `error` type from almost every function, the z3 package 9 | // mimics Z3's API by requiring you to set an error handler callback. This 10 | // error handler will be invoked whenenver an error occurs. See 11 | // ErrorHandler and Context.SetErrorHandler for more information. 12 | package z3 13 | 14 | // #cgo CFLAGS: -Ivendor/z3/src/api 15 | // #cgo LDFLAGS: ${SRCDIR}/libz3.a -lstdc++ 16 | // #include 17 | // #include "go-z3.h" 18 | import "C" 19 | -------------------------------------------------------------------------------- /z3_examples_test.go: -------------------------------------------------------------------------------- 1 | package z3 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // This example is a basic mathematical example 8 | func ExampleBasicMath() { 9 | // Create the context 10 | config := NewConfig() 11 | ctx := NewContext(config) 12 | config.Close() 13 | defer ctx.Close() 14 | 15 | // Logic: 16 | // x + y + z > 4 17 | // x + y < 2 18 | // z > 0 19 | // x != y != z 20 | // x, y, z != 0 21 | // x + y = -3 22 | 23 | // Create the solver 24 | s := ctx.NewSolver() 25 | defer s.Close() 26 | 27 | // Vars 28 | x := ctx.Const(ctx.Symbol("x"), ctx.IntSort()) 29 | y := ctx.Const(ctx.Symbol("y"), ctx.IntSort()) 30 | z := ctx.Const(ctx.Symbol("z"), ctx.IntSort()) 31 | 32 | zero := ctx.Int(0, ctx.IntSort()) // To save repeats 33 | 34 | // x + y + z > 4 35 | s.Assert(x.Add(y, z).Gt(ctx.Int(4, ctx.IntSort()))) 36 | 37 | // x + y < 2 38 | s.Assert(x.Add(y).Lt(ctx.Int(2, ctx.IntSort()))) 39 | 40 | // z > 0 41 | s.Assert(z.Gt(zero)) 42 | 43 | // x != y != z 44 | s.Assert(x.Distinct(y, z)) 45 | 46 | // x, y, z != 0 47 | s.Assert(x.Eq(zero).Not()) 48 | s.Assert(y.Eq(zero).Not()) 49 | s.Assert(z.Eq(zero).Not()) 50 | 51 | // x + y = -3 52 | s.Assert(x.Add(y).Eq(ctx.Int(-3, ctx.IntSort()))) 53 | 54 | if v := s.Check(); v != True { 55 | fmt.Println("Unsolveable") 56 | return 57 | } 58 | 59 | // Get the resulting model: 60 | m := s.Model() 61 | assignments := m.Assignments() 62 | m.Close() 63 | fmt.Printf("x = %s\n", assignments["x"]) 64 | fmt.Printf("y = %s\n", assignments["y"]) 65 | fmt.Printf("z = %s\n", assignments["z"]) 66 | 67 | // Output: 68 | // x = (- 2) 69 | // y = (- 1) 70 | // z = 8 71 | } 72 | 73 | // From C examples: demorgan 74 | func ExampleDemorgan() { 75 | // Create the context 76 | config := NewConfig() 77 | ctx := NewContext(config) 78 | config.Close() 79 | defer ctx.Close() 80 | 81 | // Create a couple variables 82 | x := ctx.Const(ctx.Symbol("x"), ctx.BoolSort()) 83 | y := ctx.Const(ctx.Symbol("y"), ctx.BoolSort()) 84 | 85 | // Final goal: !(x && y) == (!x || !y) 86 | // Built incrementally so its clearer 87 | 88 | // !(x && y) 89 | not_x_and_y := x.And(y).Not() 90 | 91 | // (!x || !y) 92 | not_x_or_not_y := x.Not().Or(y.Not()) 93 | 94 | // Conjecture and negated 95 | conj := not_x_and_y.Iff(not_x_or_not_y) 96 | negConj := conj.Not() 97 | 98 | // Create the solver 99 | s := ctx.NewSolver() 100 | defer s.Close() 101 | 102 | // Assert the constraints 103 | s.Assert(negConj) 104 | 105 | if v := s.Check(); v == False { 106 | fmt.Println("DeMorgan is valid") 107 | return 108 | } 109 | 110 | // Output: 111 | // DeMorgan is valid 112 | } 113 | 114 | // From C examples: find_model_example2 115 | func ExampleFindModel2() { 116 | // Create the context 117 | config := NewConfig() 118 | defer config.Close() 119 | ctx := NewContext(config) 120 | defer ctx.Close() 121 | 122 | // Create the solver 123 | s := ctx.NewSolver() 124 | defer s.Close() 125 | 126 | // Create a couple variables 127 | x := ctx.Const(ctx.Symbol("x"), ctx.IntSort()) 128 | y := ctx.Const(ctx.Symbol("y"), ctx.IntSort()) 129 | 130 | // Create a couple integers 131 | v1 := ctx.Const(ctx.SymbolInt(1), ctx.IntSort()) 132 | v2 := ctx.Const(ctx.SymbolInt(2), ctx.IntSort()) 133 | 134 | // y + 1 135 | y_plus_one := y.Add(v1) 136 | 137 | // x < y + 1 && x > 2 138 | c1 := x.Lt(y_plus_one) 139 | c2 := x.Gt(v2) 140 | 141 | // Assert the constraints 142 | s.Assert(c1) 143 | s.Assert(c2) 144 | 145 | { 146 | // Solve 147 | fmt.Println("Solving part 1") 148 | if v := s.Check(); v != True { 149 | fmt.Println("unsatisfied!") 150 | return 151 | } 152 | 153 | // Get the resulting model: 154 | m := s.Model() 155 | assignments := m.Assignments() 156 | m.Close() 157 | fmt.Printf("x = %s\n", assignments["x"]) 158 | fmt.Printf("y = %s\n", assignments["y"]) 159 | } 160 | 161 | // Create some new assertions 162 | // 163 | // !(x == y) 164 | c3 := x.Eq(y).Not() 165 | s.Assert(c3) 166 | 167 | { 168 | // Solve 169 | fmt.Println("\nSolving part 2") 170 | if v := s.Check(); v != True { 171 | fmt.Println("unsatisfied!") 172 | return 173 | } 174 | 175 | // Get the resulting model: 176 | m := s.Model() 177 | assignments := m.Assignments() 178 | m.Close() 179 | fmt.Printf("x = %s\n", assignments["x"]) 180 | fmt.Printf("y = %s", assignments["y"]) 181 | } 182 | 183 | // Output: 184 | // Solving part 1 185 | // x = 0 186 | // y = 1 187 | // 188 | // Solving part 2 189 | // x = 0 190 | // y = 1 191 | } 192 | --------------------------------------------------------------------------------