3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-02-17 14:21:45 +00:00

git bindings v1.0

This commit is contained in:
Nikolaj Bjorner 2026-02-15 21:24:40 -08:00
parent e2486eff77
commit 66d0fb5477
33 changed files with 5289 additions and 7 deletions

54
src/api/go/CMakeLists.txt Normal file
View file

@ -0,0 +1,54 @@
# Z3 Go API
# Note: This CMakeLists.txt is a placeholder for Go binding integration.
# Go bindings use CGO and are typically built using the Go toolchain directly.
# However, we can set up installation targets here.
if(BUILD_GO_BINDINGS)
message(STATUS "Z3 Go bindings will be installed")
# Install Go source files
install(FILES
${CMAKE_CURRENT_SOURCE_DIR}/z3.go
${CMAKE_CURRENT_SOURCE_DIR}/solver.go
${CMAKE_CURRENT_SOURCE_DIR}/go.mod
${CMAKE_CURRENT_SOURCE_DIR}/README.md
DESTINATION "${CMAKE_INSTALL_LIBDIR}/go/src/github.com/Z3Prover/z3/go"
)
# On Windows, we need to ensure the DLL is accessible
if(WIN32)
message(STATUS "Go bindings on Windows require libz3.dll in PATH")
endif()
# Add a custom target to test Go bindings if Go is available
find_program(GO_EXECUTABLE go)
if(GO_EXECUTABLE)
message(STATUS "Found Go: ${GO_EXECUTABLE}")
# Custom target to build Go bindings
add_custom_target(go-bindings
COMMAND ${CMAKE_COMMAND} -E env
CGO_CFLAGS=-I${CMAKE_SOURCE_DIR}/src/api
CGO_LDFLAGS=-L${CMAKE_BINARY_DIR} -lz3
${GO_EXECUTABLE} build -v
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Building Go bindings"
DEPENDS libz3
)
# Custom target to test Go examples
add_custom_target(test-go-examples
COMMAND ${CMAKE_COMMAND} -E env
CGO_CFLAGS=-I${CMAKE_SOURCE_DIR}/src/api
CGO_LDFLAGS=-L${CMAKE_BINARY_DIR} -lz3
LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}:$ENV{LD_LIBRARY_PATH}
PATH=${CMAKE_BINARY_DIR}\;$ENV{PATH}
${GO_EXECUTABLE} run ${CMAKE_SOURCE_DIR}/examples/go/basic_example.go
COMMENT "Running Go examples"
DEPENDS libz3
)
else()
message(STATUS "Go not found - Go bindings can be built manually")
endif()
endif()

331
src/api/go/README.md Normal file
View file

@ -0,0 +1,331 @@
# Z3 Go Bindings
This directory contains Go language bindings for the Z3 theorem prover.
## Overview
The Go bindings provide a comprehensive interface to Z3's C API using CGO. The bindings support:
- **Core Z3 Types**: Context, Config, Symbol, AST, Sort, Expr, FuncDecl
- **Solver Operations**: Creating solvers, asserting constraints, checking satisfiability
- **Model Manipulation**: Extracting and evaluating models
- **Boolean Logic**: And, Or, Not, Implies, Iff, Xor
- **Arithmetic**: Add, Sub, Mul, Div, Mod, comparison operators
- **Bit-vectors**: Full bit-vector arithmetic, bitwise operations, shifts, comparisons
- **Floating Point**: IEEE 754 floating-point arithmetic with rounding modes
- **Arrays**: Select, Store, constant arrays
- **Sequences/Strings**: String operations, concatenation, contains, indexing
- **Regular Expressions**: Pattern matching, Kleene star/plus, regex operations
- **Quantifiers**: Forall, Exists
- **Functions**: Function declarations and applications
- **Tactics & Goals**: Goal-based solving and tactic combinators
- **Probes**: Goal property checking
- **Datatypes**: Algebraic datatypes, tuples, enumerations, lists
- **Parameters**: Solver and tactic configuration
- **Optimize**: Optimization problems with maximize/minimize objectives
## Building
### Prerequisites
- Go 1.20 or later
- Z3 library built and installed
- CGO enabled
### With CMake
```bash
mkdir build && cd build
cmake -DBUILD_GO_BINDINGS=ON ..
make
```
### With Python Build System
```bash
python scripts/mk_make.py --go
cd build
make
```
## Usage
### Basic Example
```go
package main
import (
"fmt"
"github.com/Z3Prover/z3/src/api/go"
)
func main() {
// Create a context
ctx := z3.NewContext()
// Create variables
x := ctx.MkIntConst("x")
y := ctx.MkIntConst("y")
// Create constraints: x + y == 10 && x > y
ten := ctx.MkInt(10, ctx.MkIntSort())
eq := ctx.MkEq(ctx.MkAdd(x, y), ten)
gt := ctx.MkGt(x, y)
// Create solver and check
solver := ctx.NewSolver()
solver.Assert(eq)
solver.Assert(gt)
if solver.Check() == z3.Satisfiable {
model := solver.Model()
if xVal, ok := model.Eval(x, true); ok {
fmt.Println("x =", xVal.String())
}
if yVal, ok := model.Eval(y, true); ok {
fmt.Println("y =", yVal.String())
}
}
}
```
### Running Examples
```bash
cd examples/go
# Set library path (Linux/Mac)
export LD_LIBRARY_PATH=../../build:$LD_LIBRARY_PATH
export CGO_CFLAGS="-I../../src/api"
export CGO_LDFLAGS="-L../../build -lz3"
# Set library path (Windows)
set PATH=..\..\build;%PATH%
set CGO_CFLAGS=-I../../src/api
set CGO_LDFLAGS=-L../../build -lz3
# Run example
go run basic_example.go
```
## API Reference
### Context
- `NewContext()` - Create a new Z3 context
- `NewContextWithConfig(cfg *Config)` - Create context with configuration
- `SetParam(key, value string)` - Set context parameters
### Creating Expressions
- `MkBoolConst(name string)` - Create Boolean variable
- `MkIntConst(name string)` - Create integer variable
- `MkRealConst(name string)` - Create real variable
- `MkInt(value int, sort *Sort)` - Create integer constant
- `MkReal(num, den int)` - Create rational constant
### Boolean Operations
- `MkAnd(exprs ...*Expr)` - Conjunction
- `MkOr(exprs ...*Expr)` - Disjunction
- `MkNot(expr *Expr)` - Negation
- `MkImplies(lhs, rhs *Expr)` - Implication
- `MkIff(lhs, rhs *Expr)` - If-and-only-if
- `MkXor(lhs, rhs *Expr)` - Exclusive or
### Arithmetic Operations
- `MkAdd(exprs ...*Expr)` - Addition
- `MkSub(exprs ...*Expr)` - Subtraction
- `MkMul(exprs ...*Expr)` - Multiplication
- `MkDiv(lhs, rhs *Expr)` - Division
- `MkMod(lhs, rhs *Expr)` - Modulo
- `MkRem(lhs, rhs *Expr)` - Remainder
### Comparison Operations
- `MkEq(lhs, rhs *Expr)` - Equality
- `MkDistinct(exprs ...*Expr)` - Distinct
- `MkLt(lhs, rhs *Expr)` - Less than
- `MkLe(lhs, rhs *Expr)` - Less than or equal
- `MkGt(lhs, rhs *Expr)` - Greater than
- `MkGe(lhs, rhs *Expr)` - Greater than or equal
### Solver Operations
- `NewSolver()` - Create a new solver
- `Assert(constraint *Expr)` - Add constraint
- `Check()` - Check satisfiability (returns Satisfiable, Unsatisfiable, or Unknown)
- `Model()` - Get model (if satisfiable)
- `Push()` - Create backtracking point
- `Pop(n uint)` - Remove backtracking points
- `Reset()` - Remove all assertions
### Model Operations
- `Eval(expr *Expr, modelCompletion bool)` - Evaluate expression in model
- `NumConsts()` - Number of constants in model
- `NumFuncs()` - Number of functions in model
- `String()` - Get string representation
### Bit-vector Operations
- `MkBvSort(sz uint)` - Create bit-vector sort
- `MkBVConst(name string, size uint)` - Create bit-vector variable
- `MkBVAdd/Sub/Mul/UDiv/SDiv(lhs, rhs *Expr)` - Arithmetic operations
- `MkBVAnd/Or/Xor/Not(...)` - Bitwise operations
- `MkBVShl/LShr/AShr(lhs, rhs *Expr)` - Shift operations
- `MkBVULT/SLT/ULE/SLE/UGE/SGE/UGT/SGT(...)` - Comparisons
- `MkConcat(lhs, rhs *Expr)` - Bit-vector concatenation
- `MkExtract(high, low uint, expr *Expr)` - Extract bits
- `MkSignExt/ZeroExt(i uint, expr *Expr)` - Extend bit-vectors
### Floating-Point Operations
- `MkFPSort(ebits, sbits uint)` - Create floating-point sort
- `MkFPSort16/32/64/128()` - Standard IEEE 754 sorts
- `MkFPInf/NaN/Zero(sort *Sort, ...)` - Special values
- `MkFPAdd/Sub/Mul/Div(rm, lhs, rhs *Expr)` - Arithmetic with rounding
- `MkFPNeg/Abs/Sqrt(...)` - Unary operations
- `MkFPLT/GT/LE/GE/Eq(lhs, rhs *Expr)` - Comparisons
- `MkFPIsNaN/IsInf/IsZero(expr *Expr)` - Predicates
### Sequence/String Operations
- `MkStringSort()` - Create string sort
- `MkSeqSort(elemSort *Sort)` - Create sequence sort
- `MkString(value string)` - Create string constant
- `MkSeqConcat(exprs ...*Expr)` - Concatenation
- `MkSeqLength(seq *Expr)` - Length
- `MkSeqPrefix/Suffix/Contains(...)` - Predicates
- `MkSeqAt(seq, index *Expr)` - Element access
- `MkSeqExtract(seq, offset, length *Expr)` - Substring
- `MkStrToInt/IntToStr(...)` - Conversions
### Regular Expression Operations
- `MkReSort(seqSort *Sort)` - Create regex sort
- `MkToRe(seq *Expr)` - Convert string to regex
- `MkInRe(seq, re *Expr)` - String matches regex predicate
- `MkReStar(re *Expr)` - Kleene star (zero or more)
- `MkRePlus(re *Expr)` - Kleene plus (one or more)
- `MkReOption(re *Expr)` - Optional (zero or one)
- `MkRePower(re *Expr, n uint)` - Exactly n repetitions
- `MkReLoop(re *Expr, lo, hi uint)` - Bounded repetition
- `MkReConcat(regexes ...*Expr)` - Concatenation
- `MkReUnion(regexes ...*Expr)` - Alternation (OR)
- `MkReIntersect(regexes ...*Expr)` - Intersection
- `MkReComplement(re *Expr)` - Complement
- `MkReDiff(a, b *Expr)` - Difference
- `MkReEmpty/Full/Allchar(sort *Sort)` - Special regexes
- `MkReRange(lo, hi *Expr)` - Character range
- `MkSeqReplaceRe/ReAll(seq, re, replacement *Expr)` - Regex replace
### Datatype Operations
- `MkConstructor(name, recognizer string, ...)` - Create constructor
- `MkDatatypeSort(name string, constructors []*Constructor)` - Create datatype
- `MkDatatypeSorts(names []string, ...)` - Mutually recursive datatypes
- `MkTupleSort(name string, fieldNames []string, fieldSorts []*Sort)` - Tuples
- `MkEnumSort(name string, enumNames []string)` - Enumerations
- `MkListSort(name string, elemSort *Sort)` - Lists
### Tactic Operations
- `MkTactic(name string)` - Create tactic by name
- `MkGoal(models, unsatCores, proofs bool)` - Create goal
- `Apply(g *Goal)` - Apply tactic to goal
- `AndThen(t2 *Tactic)` - Sequential composition
- `OrElse(t2 *Tactic)` - Try first, fallback to second
- `Repeat(max uint)` - Repeat tactic
- `TacticWhen/Cond(...)` - Conditional tactics
### Probe Operations
- `MkProbe(name string)` - Create probe by name
- `Apply(g *Goal)` - Evaluate probe on goal
- `Lt/Gt/Le/Ge/Eq(p2 *Probe)` - Probe comparisons
- `And/Or/Not(...)` - Probe combinators
### Parameter Operations
- `MkParams()` - Create parameter set
- `SetBool/Uint/Double/Symbol(key string, value ...)` - Set parameters
### Optimize Operations
- `NewOptimize()` - Create optimization context
- `Assert(constraint *Expr)` - Add constraint
- `AssertSoft(constraint *Expr, weight, group string)` - Add soft constraint
- `Maximize(expr *Expr)` - Add maximization objective
- `Minimize(expr *Expr)` - Add minimization objective
- `Check(assumptions ...*Expr)` - Check and optimize
- `Model()` - Get optimal model
- `GetLower/Upper(index uint)` - Get objective bounds
- `Push/Pop()` - Backtracking
- `Assertions/Objectives()` - Get assertions and objectives
- `UnsatCore()` - Get unsat core
### Fixedpoint Operations (Datalog/CHC)
- `NewFixedpoint()` - Create fixedpoint solver
- `RegisterRelation(funcDecl *FuncDecl)` - Register predicate
- `AddRule(rule *Expr, name *Symbol)` - Add Horn clause
- `AddFact(pred *FuncDecl, args []int)` - Add table fact
- `Query(query *Expr)` - Query constraints
- `QueryRelations(relations []*FuncDecl)` - Query relations
- `GetAnswer()` - Get satisfying instance or proof
- `Push/Pop()` - Backtracking
### Quantifier Operations
- `MkQuantifier(isForall bool, weight int, sorts, names, body, patterns)` - Create quantifier
- `MkQuantifierConst(isForall bool, weight int, bound, body, patterns)` - Create with bound vars
- `IsUniversal/IsExistential()` - Check quantifier type
- `GetNumBound()` - Number of bound variables
- `GetBoundName/Sort(idx int)` - Get bound variable info
- `GetBody()` - Get quantifier body
- `GetNumPatterns()` - Number of patterns
- `GetPattern(idx int)` - Get pattern
### Lambda Operations
- `MkLambda(sorts, names, body)` - Create lambda expression
- `MkLambdaConst(bound, body)` - Create lambda with bound variables
- `GetNumBound()` - Number of bound variables
- `GetBoundName/Sort(idx int)` - Get bound variable info
- `GetBody()` - Get lambda body
### Type Variables
- `MkTypeVariable(name *Symbol)` - Create polymorphic type variable sort
### Logging
- `OpenLog(filename string)` - Open interaction log
- `CloseLog()` - Close log
- `AppendLog(s string)` - Append to log
- `IsLogOpen()` - Check if log is open
## Memory Management
The Go bindings use `runtime.SetFinalizer` to automatically manage Z3 reference counts. You don't need to manually call inc_ref/dec_ref. However, be aware that finalizers run during garbage collection, so resources may not be freed immediately.
## Thread Safety
Z3 contexts are not thread-safe. Each goroutine should use its own context, or use appropriate synchronization when sharing a context.
## License
Z3 is licensed under the MIT License. See LICENSE.txt in the repository root.
## Contributing
Bug reports and contributions are welcome! Please submit issues and pull requests to the main Z3 repository.
## References
- [Z3 GitHub Repository](https://github.com/Z3Prover/z3)
- [Z3 API Documentation](https://z3prover.github.io/api/html/index.html)
- [Z3 Guide](https://microsoft.github.io/z3guide/)

89
src/api/go/add_godoc.py Normal file
View file

@ -0,0 +1,89 @@
#!/usr/bin/env python3
"""
Add godoc comments to Z3 Go bindings systematically.
This script adds proper godoc documentation to all exported types and functions.
"""
import os
import re
# Godoc comment templates for common patterns
TYPE_COMMENTS = {
'Config': '// Config represents a Z3 configuration object used to customize solver behavior.\n// Create with NewConfig and configure using SetParamValue before creating a Context.',
'Context': '// Context represents a Z3 logical context.\n// All Z3 objects (sorts, expressions, solvers) are tied to the context that created them.\n// Contexts are not thread-safe - use separate contexts for concurrent operations.',
'Symbol': '// Symbol represents a Z3 symbol, which can be either a string or integer identifier.\n// Symbols are used to name sorts, constants, and functions.',
'AST': '// AST represents an Abstract Syntax Tree node in Z3.\n// This is the base type for all Z3 expressions, sorts, and function declarations.',
'Sort': '// Sort represents a type in Z3\'s type system.\n// Common sorts include Bool, Int, Real, BitVec, Array, and user-defined datatypes.',
'Expr': '// Expr represents a Z3 expression (term).\n// Expressions are typed AST nodes that can be evaluated, simplified, or used in constraints.',
'FuncDecl': '// FuncDecl represents a function declaration in Z3.\n// Function declarations define the signature (domain and range sorts) of functions.',
'Pattern': '// Pattern represents a pattern for quantifier instantiation.\n// Patterns guide Z3\'s E-matching algorithm for quantifier instantiation.',
'Quantifier': '// Quantifier represents a quantified formula (forall or exists).\n// Quantifiers bind variables and include optional patterns for instantiation.',
'Lambda': '// Lambda represents a lambda expression (anonymous function).\n// Lambda expressions can be used as array values or in higher-order reasoning.',
'Statistics': '// Statistics holds performance and diagnostic information from Z3 solvers.\n// Use GetKey, GetUintValue, and GetDoubleValue to access individual statistics.',
}
FUNCTION_COMMENTS = {
'NewConfig': '// NewConfig creates a new Z3 configuration object.\n// Use SetParamValue to configure parameters before creating a context.',
'NewContext': '// NewContext creates a new Z3 context with default configuration.\n// The context manages memory for all Z3 objects and must outlive any objects it creates.',
'NewContextWithConfig': '// NewContextWithConfig creates a new Z3 context with the given configuration.\n// The configuration is consumed and should not be reused.',
}
def add_godoc_comment(content, pattern, comment):
"""Add godoc comment before a type or function declaration."""
# Check if comment already exists
lines = content.split('\n')
result = []
i = 0
while i < len(lines):
line = lines[i]
# Check if this line matches our pattern
if re.match(pattern, line):
# Check if previous line is already a comment
if i > 0 and (result[-1].strip().startswith('//') or result[-1].strip().startswith('/*')):
# Comment exists, skip
result.append(line)
else:
# Add comment
result.append(comment)
result.append(line)
else:
result.append(line)
i += 1
return '\n'.join(result)
def process_file(filepath, type_comments, func_comments):
"""Process a single Go file and add godoc comments."""
print(f"Processing {filepath}...")
with open(filepath, 'r', encoding='utf-8') as f:
content = f.read()
# Add type comments
for type_name, comment in type_comments.items():
pattern = f'^type {type_name} struct'
content = add_godoc_comment(content, pattern, comment)
# Add function comments
for func_name, comment in func_comments.items():
pattern = f'^func (\\([^)]+\\) )?{func_name}\\('
content = add_godoc_comment(content, pattern, comment)
with open(filepath, 'w', encoding='utf-8') as f:
f.write(content)
print(f"Updated {filepath}")
if __name__ == '__main__':
go_api_dir = os.path.dirname(os.path.abspath(__file__))
# Process z3.go with core types
z3_go = os.path.join(go_api_dir, 'z3.go')
if os.path.exists(z3_go):
process_file(z3_go, TYPE_COMMENTS, FUNCTION_COMMENTS)
print("\nGodoc comments added successfully!")
print("Run 'go doc' to verify documentation.")

126
src/api/go/arith.go Normal file
View file

@ -0,0 +1,126 @@
package z3
/*
#include "z3.h"
#include <stdlib.h>
*/
import "C"
// Arithmetic operations and sorts
// MkIntSort creates the integer sort.
func (c *Context) MkIntSort() *Sort {
return newSort(c, C.Z3_mk_int_sort(c.ptr))
}
// MkRealSort creates the real number sort.
func (c *Context) MkRealSort() *Sort {
return newSort(c, C.Z3_mk_real_sort(c.ptr))
}
// MkInt creates an integer constant from an int.
func (c *Context) MkInt(value int, sort *Sort) *Expr {
return newExpr(c, C.Z3_mk_int(c.ptr, C.int(value), sort.ptr))
}
// MkInt64 creates an integer constant from an int64.
func (c *Context) MkInt64(value int64, sort *Sort) *Expr {
return newExpr(c, C.Z3_mk_int64(c.ptr, C.int64_t(value), sort.ptr))
}
// MkReal creates a real constant from numerator and denominator.
func (c *Context) MkReal(num, den int) *Expr {
return newExpr(c, C.Z3_mk_real(c.ptr, C.int(num), C.int(den)))
}
// MkIntConst creates an integer constant (variable) with the given name.
func (c *Context) MkIntConst(name string) *Expr {
sym := c.MkStringSymbol(name)
return c.MkConst(sym, c.MkIntSort())
}
// MkRealConst creates a real constant (variable) with the given name.
func (c *Context) MkRealConst(name string) *Expr {
sym := c.MkStringSymbol(name)
return c.MkConst(sym, c.MkRealSort())
}
// MkAdd creates an addition.
func (c *Context) MkAdd(exprs ...*Expr) *Expr {
if len(exprs) == 0 {
return c.MkInt(0, c.MkIntSort())
}
if len(exprs) == 1 {
return exprs[0]
}
cExprs := make([]C.Z3_ast, len(exprs))
for i, e := range exprs {
cExprs[i] = e.ptr
}
return newExpr(c, C.Z3_mk_add(c.ptr, C.uint(len(exprs)), &cExprs[0]))
}
// MkSub creates a subtraction.
func (c *Context) MkSub(exprs ...*Expr) *Expr {
if len(exprs) == 0 {
return c.MkInt(0, c.MkIntSort())
}
if len(exprs) == 1 {
return newExpr(c, C.Z3_mk_unary_minus(c.ptr, exprs[0].ptr))
}
cExprs := make([]C.Z3_ast, len(exprs))
for i, e := range exprs {
cExprs[i] = e.ptr
}
return newExpr(c, C.Z3_mk_sub(c.ptr, C.uint(len(exprs)), &cExprs[0]))
}
// MkMul creates a multiplication.
func (c *Context) MkMul(exprs ...*Expr) *Expr {
if len(exprs) == 0 {
return c.MkInt(1, c.MkIntSort())
}
if len(exprs) == 1 {
return exprs[0]
}
cExprs := make([]C.Z3_ast, len(exprs))
for i, e := range exprs {
cExprs[i] = e.ptr
}
return newExpr(c, C.Z3_mk_mul(c.ptr, C.uint(len(exprs)), &cExprs[0]))
}
// MkDiv creates a division.
func (c *Context) MkDiv(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_div(c.ptr, lhs.ptr, rhs.ptr))
}
// MkMod creates a modulo operation.
func (c *Context) MkMod(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_mod(c.ptr, lhs.ptr, rhs.ptr))
}
// MkRem creates a remainder operation.
func (c *Context) MkRem(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_rem(c.ptr, lhs.ptr, rhs.ptr))
}
// MkLt creates a less-than constraint.
func (c *Context) MkLt(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_lt(c.ptr, lhs.ptr, rhs.ptr))
}
// MkLe creates a less-than-or-equal constraint.
func (c *Context) MkLe(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_le(c.ptr, lhs.ptr, rhs.ptr))
}
// MkGt creates a greater-than constraint.
func (c *Context) MkGt(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_gt(c.ptr, lhs.ptr, rhs.ptr))
}
// MkGe creates a greater-than-or-equal constraint.
func (c *Context) MkGe(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_ge(c.ptr, lhs.ptr, rhs.ptr))
}

29
src/api/go/array.go Normal file
View file

@ -0,0 +1,29 @@
package z3
/*
#include "z3.h"
#include <stdlib.h>
*/
import "C"
// Array operations and sorts
// MkArraySort creates an array sort.
func (c *Context) MkArraySort(domain, range_ *Sort) *Sort {
return newSort(c, C.Z3_mk_array_sort(c.ptr, domain.ptr, range_.ptr))
}
// MkSelect creates an array read (select) operation.
func (c *Context) MkSelect(array, index *Expr) *Expr {
return newExpr(c, C.Z3_mk_select(c.ptr, array.ptr, index.ptr))
}
// MkStore creates an array write (store) operation.
func (c *Context) MkStore(array, index, value *Expr) *Expr {
return newExpr(c, C.Z3_mk_store(c.ptr, array.ptr, index.ptr, value.ptr))
}
// MkConstArray creates a constant array.
func (c *Context) MkConstArray(sort *Sort, value *Expr) *Expr {
return newExpr(c, C.Z3_mk_const_array(c.ptr, sort.ptr, value.ptr))
}

160
src/api/go/bitvec.go Normal file
View file

@ -0,0 +1,160 @@
package z3
/*
#include "z3.h"
#include <stdlib.h>
*/
import "C"
// Bit-vector operations
// MkBVConst creates a bit-vector constant with the given name and size.
func (c *Context) MkBVConst(name string, size uint) *Expr {
sym := c.MkStringSymbol(name)
return c.MkConst(sym, c.MkBvSort(size))
}
// MkBV creates a bit-vector numeral from an integer.
func (c *Context) MkBV(value int, size uint) *Expr {
return newExpr(c, C.Z3_mk_int(c.ptr, C.int(value), c.MkBvSort(size).ptr))
}
// MkBVFromInt64 creates a bit-vector from an int64.
func (c *Context) MkBVFromInt64(value int64, size uint) *Expr {
return newExpr(c, C.Z3_mk_int64(c.ptr, C.int64_t(value), c.MkBvSort(size).ptr))
}
// MkBVAdd creates a bit-vector addition.
func (c *Context) MkBVAdd(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvadd(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVSub creates a bit-vector subtraction.
func (c *Context) MkBVSub(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvsub(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVMul creates a bit-vector multiplication.
func (c *Context) MkBVMul(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvmul(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVUDiv creates an unsigned bit-vector division.
func (c *Context) MkBVUDiv(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvudiv(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVSDiv creates a signed bit-vector division.
func (c *Context) MkBVSDiv(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvsdiv(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVURem creates an unsigned bit-vector remainder.
func (c *Context) MkBVURem(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvurem(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVSRem creates a signed bit-vector remainder.
func (c *Context) MkBVSRem(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvsrem(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVNeg creates a bit-vector negation.
func (c *Context) MkBVNeg(expr *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvneg(c.ptr, expr.ptr))
}
// MkBVAnd creates a bit-vector bitwise AND.
func (c *Context) MkBVAnd(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvand(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVOr creates a bit-vector bitwise OR.
func (c *Context) MkBVOr(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvor(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVXor creates a bit-vector bitwise XOR.
func (c *Context) MkBVXor(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvxor(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVNot creates a bit-vector bitwise NOT.
func (c *Context) MkBVNot(expr *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvnot(c.ptr, expr.ptr))
}
// MkBVShl creates a bit-vector shift left.
func (c *Context) MkBVShl(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvshl(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVLShr creates a bit-vector logical shift right.
func (c *Context) MkBVLShr(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvlshr(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVAShr creates a bit-vector arithmetic shift right.
func (c *Context) MkBVAShr(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvashr(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVULT creates an unsigned bit-vector less-than.
func (c *Context) MkBVULT(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvult(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVSLT creates a signed bit-vector less-than.
func (c *Context) MkBVSLT(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvslt(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVULE creates an unsigned bit-vector less-than-or-equal.
func (c *Context) MkBVULE(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvule(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVSLE creates a signed bit-vector less-than-or-equal.
func (c *Context) MkBVSLE(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvsle(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVUGE creates an unsigned bit-vector greater-than-or-equal.
func (c *Context) MkBVUGE(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvuge(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVSGE creates a signed bit-vector greater-than-or-equal.
func (c *Context) MkBVSGE(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvsge(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVUGT creates an unsigned bit-vector greater-than.
func (c *Context) MkBVUGT(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvugt(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVSGT creates a signed bit-vector greater-than.
func (c *Context) MkBVSGT(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvsgt(c.ptr, lhs.ptr, rhs.ptr))
}
// MkConcat creates a bit-vector concatenation.
func (c *Context) MkConcat(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_concat(c.ptr, lhs.ptr, rhs.ptr))
}
// MkExtract creates a bit-vector extraction.
func (c *Context) MkExtract(high, low uint, expr *Expr) *Expr {
return newExpr(c, C.Z3_mk_extract(c.ptr, C.uint(high), C.uint(low), expr.ptr))
}
// MkSignExt creates a bit-vector sign extension.
func (c *Context) MkSignExt(i uint, expr *Expr) *Expr {
return newExpr(c, C.Z3_mk_sign_ext(c.ptr, C.uint(i), expr.ptr))
}
// MkZeroExt creates a bit-vector zero extension.
func (c *Context) MkZeroExt(i uint, expr *Expr) *Expr {
return newExpr(c, C.Z3_mk_zero_ext(c.ptr, C.uint(i), expr.ptr))
}

293
src/api/go/datatype.go Normal file
View file

@ -0,0 +1,293 @@
package z3
/*
#include "z3.h"
#include <stdlib.h>
*/
import "C"
import (
"runtime"
"unsafe"
)
// Constructor represents a datatype constructor.
type Constructor struct {
ctx *Context
ptr C.Z3_constructor
}
// newConstructor creates a new Constructor and manages its reference count.
func newConstructor(ctx *Context, ptr C.Z3_constructor) *Constructor {
c := &Constructor{ctx: ctx, ptr: ptr}
C.Z3_constructor_inc_ref(ctx.ptr, ptr)
runtime.SetFinalizer(c, func(cons *Constructor) {
C.Z3_constructor_dec_ref(cons.ctx.ptr, cons.ptr)
})
return c
}
// MkConstructor creates a constructor for a datatype.
// name is the constructor name, recognizer is the recognizer name,
// fieldNames are the names of the fields, and fieldSorts are the sorts of the fields.
// fieldSortRefs can be 0 for non-recursive fields or the datatype index for recursive fields.
func (c *Context) MkConstructor(name, recognizer string, fieldNames []string, fieldSorts []*Sort, fieldSortRefs []uint) *Constructor {
cName := C.CString(name)
cRecognizer := C.CString(recognizer)
defer C.free(unsafe.Pointer(cName))
defer C.free(unsafe.Pointer(cRecognizer))
numFields := uint(len(fieldNames))
if numFields != uint(len(fieldSorts)) || numFields != uint(len(fieldSortRefs)) {
panic("fieldNames, fieldSorts, and fieldSortRefs must have the same length")
}
var cFieldNames *C.Z3_symbol
var cSorts *C.Z3_sort
var cSortRefs *C.uint
if numFields > 0 {
fieldSyms := make([]C.Z3_symbol, numFields)
for i, fname := range fieldNames {
fieldSyms[i] = c.MkStringSymbol(fname).ptr
}
cFieldNames = &fieldSyms[0]
sorts := make([]C.Z3_sort, numFields)
for i, s := range fieldSorts {
if s != nil {
sorts[i] = s.ptr
}
}
cSorts = &sorts[0]
refs := make([]C.uint, numFields)
for i, r := range fieldSortRefs {
refs[i] = C.uint(r)
}
cSortRefs = &refs[0]
}
sym := c.MkStringSymbol(name)
recSym := c.MkStringSymbol(recognizer)
return newConstructor(c, C.Z3_mk_constructor(
c.ptr,
sym.ptr,
recSym.ptr,
C.uint(numFields),
cFieldNames,
cSorts,
cSortRefs,
))
}
// ConstructorList represents a list of datatype constructors.
type ConstructorList struct {
ctx *Context
ptr C.Z3_constructor_list
}
// newConstructorList creates a new ConstructorList and manages its reference count.
func newConstructorList(ctx *Context, ptr C.Z3_constructor_list) *ConstructorList {
cl := &ConstructorList{ctx: ctx, ptr: ptr}
C.Z3_constructor_list_inc_ref(ctx.ptr, ptr)
runtime.SetFinalizer(cl, func(list *ConstructorList) {
C.Z3_constructor_list_dec_ref(list.ctx.ptr, list.ptr)
})
return cl
}
// MkConstructorList creates a list of constructors for a datatype.
func (c *Context) MkConstructorList(constructors []*Constructor) *ConstructorList {
numCons := uint(len(constructors))
if numCons == 0 {
return nil
}
cons := make([]C.Z3_constructor, numCons)
for i, constr := range constructors {
cons[i] = constr.ptr
}
return newConstructorList(c, C.Z3_mk_constructor_list(c.ptr, C.uint(numCons), &cons[0]))
}
// MkDatatypeSort creates a datatype sort from a constructor list.
func (c *Context) MkDatatypeSort(name string, constructors []*Constructor) *Sort {
sym := c.MkStringSymbol(name)
numCons := uint(len(constructors))
cons := make([]C.Z3_constructor, numCons)
for i, constr := range constructors {
cons[i] = constr.ptr
}
return newSort(c, C.Z3_mk_datatype(c.ptr, sym.ptr, C.uint(numCons), &cons[0]))
}
// MkDatatypeSorts creates multiple mutually recursive datatype sorts.
func (c *Context) MkDatatypeSorts(names []string, constructorLists [][]*Constructor) []*Sort {
numTypes := uint(len(names))
if numTypes != uint(len(constructorLists)) {
panic("names and constructorLists must have the same length")
}
syms := make([]C.Z3_symbol, numTypes)
for i, name := range names {
syms[i] = c.MkStringSymbol(name).ptr
}
cLists := make([]C.Z3_constructor_list, numTypes)
for i, constrs := range constructorLists {
cons := make([]C.Z3_constructor, len(constrs))
for j, constr := range constrs {
cons[j] = constr.ptr
}
cLists[i] = C.Z3_mk_constructor_list(c.ptr, C.uint(len(constrs)), &cons[0])
}
resultSorts := make([]C.Z3_sort, numTypes)
C.Z3_mk_datatypes(c.ptr, C.uint(numTypes), &syms[0], &resultSorts[0], &cLists[0])
// Clean up constructor lists
for i := range cLists {
C.Z3_constructor_list_dec_ref(c.ptr, cLists[i])
}
sorts := make([]*Sort, numTypes)
for i := range resultSorts {
sorts[i] = newSort(c, resultSorts[i])
}
return sorts
}
// GetDatatypeSortConstructor returns the i-th constructor of a datatype sort.
func (c *Context) GetDatatypeSortConstructor(sort *Sort, i uint) *FuncDecl {
return newFuncDecl(c, C.Z3_get_datatype_sort_constructor(c.ptr, sort.ptr, C.uint(i)))
}
// GetDatatypeSortRecognizer returns the i-th recognizer of a datatype sort.
func (c *Context) GetDatatypeSortRecognizer(sort *Sort, i uint) *FuncDecl {
return newFuncDecl(c, C.Z3_get_datatype_sort_recognizer(c.ptr, sort.ptr, C.uint(i)))
}
// GetDatatypeSortConstructorAccessor returns the accessor for the i-th field of the j-th constructor.
func (c *Context) GetDatatypeSortConstructorAccessor(sort *Sort, constructorIdx, accessorIdx uint) *FuncDecl {
return newFuncDecl(c, C.Z3_get_datatype_sort_constructor_accessor(
c.ptr, sort.ptr, C.uint(constructorIdx), C.uint(accessorIdx)))
}
// GetDatatypeSortNumConstructors returns the number of constructors in a datatype sort.
func (c *Context) GetDatatypeSortNumConstructors(sort *Sort) uint {
return uint(C.Z3_get_datatype_sort_num_constructors(c.ptr, sort.ptr))
}
// Tuple sorts (special case of datatypes)
// MkTupleSort creates a tuple sort with the given field sorts.
func (c *Context) MkTupleSort(name string, fieldNames []string, fieldSorts []*Sort) (*Sort, *FuncDecl, []*FuncDecl) {
sym := c.MkStringSymbol(name)
numFields := uint(len(fieldNames))
if numFields != uint(len(fieldSorts)) {
panic("fieldNames and fieldSorts must have the same length")
}
fieldSyms := make([]C.Z3_symbol, numFields)
for i, fname := range fieldNames {
fieldSyms[i] = c.MkStringSymbol(fname).ptr
}
sorts := make([]C.Z3_sort, numFields)
for i, s := range fieldSorts {
sorts[i] = s.ptr
}
var mkTupleDecl C.Z3_func_decl
projDecls := make([]C.Z3_func_decl, numFields)
tupleSort := C.Z3_mk_tuple_sort(
c.ptr,
sym.ptr,
C.uint(numFields),
&fieldSyms[0],
&sorts[0],
&mkTupleDecl,
&projDecls[0],
)
projections := make([]*FuncDecl, numFields)
for i := range projDecls {
projections[i] = newFuncDecl(c, projDecls[i])
}
return newSort(c, tupleSort), newFuncDecl(c, mkTupleDecl), projections
}
// Enumeration sorts (special case of datatypes)
// MkEnumSort creates an enumeration sort with the given constants.
func (c *Context) MkEnumSort(name string, enumNames []string) (*Sort, []*FuncDecl, []*FuncDecl) {
sym := c.MkStringSymbol(name)
numEnums := uint(len(enumNames))
enumSyms := make([]C.Z3_symbol, numEnums)
for i, ename := range enumNames {
enumSyms[i] = c.MkStringSymbol(ename).ptr
}
enumConsts := make([]C.Z3_func_decl, numEnums)
enumTesters := make([]C.Z3_func_decl, numEnums)
enumSort := C.Z3_mk_enumeration_sort(
c.ptr,
sym.ptr,
C.uint(numEnums),
&enumSyms[0],
&enumConsts[0],
&enumTesters[0],
)
consts := make([]*FuncDecl, numEnums)
for i := range enumConsts {
consts[i] = newFuncDecl(c, enumConsts[i])
}
testers := make([]*FuncDecl, numEnums)
for i := range enumTesters {
testers[i] = newFuncDecl(c, enumTesters[i])
}
return newSort(c, enumSort), consts, testers
}
// List sorts (special case of datatypes)
// MkListSort creates a list sort with the given element sort.
func (c *Context) MkListSort(name string, elemSort *Sort) (*Sort, *FuncDecl, *FuncDecl, *FuncDecl, *FuncDecl, *FuncDecl, *FuncDecl) {
sym := c.MkStringSymbol(name)
var nilDecl, consDecl, isNilDecl, isConsDecl, headDecl, tailDecl C.Z3_func_decl
listSort := C.Z3_mk_list_sort(
c.ptr,
sym.ptr,
elemSort.ptr,
&nilDecl,
&isNilDecl,
&consDecl,
&isConsDecl,
&headDecl,
&tailDecl,
)
return newSort(c, listSort),
newFuncDecl(c, nilDecl),
newFuncDecl(c, consDecl),
newFuncDecl(c, isNilDecl),
newFuncDecl(c, isConsDecl),
newFuncDecl(c, headDecl),
newFuncDecl(c, tailDecl)
}

282
src/api/go/fixedpoint.go Normal file
View file

@ -0,0 +1,282 @@
// Copyright (c) Microsoft Corporation 2025
// Z3 Go API: Fixedpoint solver for Datalog and CHC (Constrained Horn Clauses)
package z3
/*
#include "z3.h"
#include <stdlib.h>
*/
import "C"
import (
"runtime"
"unsafe"
)
// Fixedpoint represents a fixedpoint solver for Datalog/CHC queries
type Fixedpoint struct {
ctx *Context
ptr C.Z3_fixedpoint
}
// newFixedpoint creates a new Fixedpoint solver with proper memory management
func newFixedpoint(ctx *Context, ptr C.Z3_fixedpoint) *Fixedpoint {
fp := &Fixedpoint{ctx: ctx, ptr: ptr}
C.Z3_fixedpoint_inc_ref(ctx.ptr, ptr)
runtime.SetFinalizer(fp, func(f *Fixedpoint) {
C.Z3_fixedpoint_dec_ref(f.ctx.ptr, f.ptr)
})
return fp
}
// NewFixedpoint creates a new fixedpoint solver
func (ctx *Context) NewFixedpoint() *Fixedpoint {
ptr := C.Z3_mk_fixedpoint(ctx.ptr)
return newFixedpoint(ctx, ptr)
}
// GetHelp returns a string describing all available fixedpoint solver parameters
func (f *Fixedpoint) GetHelp() string {
cstr := C.Z3_fixedpoint_get_help(f.ctx.ptr, f.ptr)
return C.GoString(cstr)
}
// SetParams sets the fixedpoint solver parameters
func (f *Fixedpoint) SetParams(params *Params) {
C.Z3_fixedpoint_set_params(f.ctx.ptr, f.ptr, params.ptr)
}
// GetParamDescrs retrieves parameter descriptions for the fixedpoint solver
func (f *Fixedpoint) GetParamDescrs() *ParamDescrs {
ptr := C.Z3_fixedpoint_get_param_descrs(f.ctx.ptr, f.ptr)
return newParamDescrs(f.ctx, ptr)
}
// Assert adds a constraint into the fixedpoint solver
func (f *Fixedpoint) Assert(constraint *Expr) {
C.Z3_fixedpoint_assert(f.ctx.ptr, f.ptr, constraint.ptr)
}
// RegisterRelation registers a predicate as a recursive relation
func (f *Fixedpoint) RegisterRelation(funcDecl *FuncDecl) {
C.Z3_fixedpoint_register_relation(f.ctx.ptr, f.ptr, funcDecl.ptr)
}
// AddRule adds a rule (Horn clause) to the fixedpoint solver
// The rule should be an implication of the form body => head
// where head is a relation and body is a conjunction of relations
func (f *Fixedpoint) AddRule(rule *Expr, name *Symbol) {
var namePtr C.Z3_symbol
if name != nil {
namePtr = name.ptr
} else {
namePtr = 0
}
C.Z3_fixedpoint_add_rule(f.ctx.ptr, f.ptr, rule.ptr, namePtr)
}
// AddFact adds a table fact to the fixedpoint solver
func (f *Fixedpoint) AddFact(pred *FuncDecl, args []int) {
if len(args) == 0 {
C.Z3_fixedpoint_add_fact(f.ctx.ptr, f.ptr, pred.ptr, 0, nil)
return
}
cArgs := make([]C.uint, len(args))
for i, arg := range args {
cArgs[i] = C.uint(arg)
}
C.Z3_fixedpoint_add_fact(f.ctx.ptr, f.ptr, pred.ptr, C.uint(len(args)), &cArgs[0])
}
// Query queries the fixedpoint solver with a constraint
// Returns Satisfiable if there is a derivation, Unsatisfiable if not
func (f *Fixedpoint) Query(query *Expr) Status {
result := C.Z3_fixedpoint_query(f.ctx.ptr, f.ptr, query.ptr)
switch result {
case C.Z3_L_TRUE:
return Satisfiable
case C.Z3_L_FALSE:
return Unsatisfiable
default:
return Unknown
}
}
// QueryRelations queries the fixedpoint solver with an array of relations
// Returns Satisfiable if any relation is non-empty, Unsatisfiable otherwise
func (f *Fixedpoint) QueryRelations(relations []*FuncDecl) Status {
if len(relations) == 0 {
return Unknown
}
cRelations := make([]C.Z3_func_decl, len(relations))
for i, rel := range relations {
cRelations[i] = rel.ptr
}
result := C.Z3_fixedpoint_query_relations(f.ctx.ptr, f.ptr, C.uint(len(relations)), &cRelations[0])
switch result {
case C.Z3_L_TRUE:
return Satisfiable
case C.Z3_L_FALSE:
return Unsatisfiable
default:
return Unknown
}
}
// UpdateRule updates a named rule in the fixedpoint solver
func (f *Fixedpoint) UpdateRule(rule *Expr, name *Symbol) {
var namePtr C.Z3_symbol
if name != nil {
namePtr = name.ptr
} else {
namePtr = 0
}
C.Z3_fixedpoint_update_rule(f.ctx.ptr, f.ptr, rule.ptr, namePtr)
}
// GetAnswer retrieves the satisfying instance or instances of solver,
// or definitions for the recursive predicates that show unsatisfiability
func (f *Fixedpoint) GetAnswer() *Expr {
ptr := C.Z3_fixedpoint_get_answer(f.ctx.ptr, f.ptr)
if ptr == nil {
return nil
}
return newExpr(f.ctx, ptr)
}
// GetReasonUnknown retrieves explanation why fixedpoint engine returned status Unknown
func (f *Fixedpoint) GetReasonUnknown() string {
cstr := C.Z3_fixedpoint_get_reason_unknown(f.ctx.ptr, f.ptr)
return C.GoString(cstr)
}
// GetNumLevels retrieves the number of levels explored for a given predicate
func (f *Fixedpoint) GetNumLevels(predicate *FuncDecl) int {
return int(C.Z3_fixedpoint_get_num_levels(f.ctx.ptr, f.ptr, predicate.ptr))
}
// GetCoverDelta retrieves the cover delta for a given predicate and level
func (f *Fixedpoint) GetCoverDelta(level int, predicate *FuncDecl) *Expr {
ptr := C.Z3_fixedpoint_get_cover_delta(f.ctx.ptr, f.ptr, C.int(level), predicate.ptr)
if ptr == nil {
return nil
}
return newExpr(f.ctx, ptr)
}
// AddCover adds a cover constraint to a predicate at a given level
func (f *Fixedpoint) AddCover(level int, predicate *FuncDecl, property *Expr) {
C.Z3_fixedpoint_add_cover(f.ctx.ptr, f.ptr, C.int(level), predicate.ptr, property.ptr)
}
// String returns the string representation of the fixedpoint solver
func (f *Fixedpoint) String() string {
cstr := C.Z3_fixedpoint_to_string(f.ctx.ptr, f.ptr, 0, nil)
return C.GoString(cstr)
}
// GetStatistics retrieves statistics for the fixedpoint solver
func (f *Fixedpoint) GetStatistics() *Statistics {
ptr := C.Z3_fixedpoint_get_statistics(f.ctx.ptr, f.ptr)
return newStatistics(f.ctx, ptr)
}
// GetRules retrieves the current rules as a string
func (f *Fixedpoint) GetRules() string {
return f.String()
}
// GetAssertions retrieves the fixedpoint assertions as an AST vector
func (f *Fixedpoint) GetAssertions() *ASTVector {
ptr := C.Z3_fixedpoint_get_assertions(f.ctx.ptr, f.ptr)
return newASTVector(f.ctx, ptr)
}
// Push creates a backtracking point
func (f *Fixedpoint) Push() {
C.Z3_fixedpoint_push(f.ctx.ptr, f.ptr)
}
// Pop backtracks one backtracking point
func (f *Fixedpoint) Pop() {
C.Z3_fixedpoint_pop(f.ctx.ptr, f.ptr)
}
// SetPredicateRepresentation sets the predicate representation for a given relation
func (f *Fixedpoint) SetPredicateRepresentation(funcDecl *FuncDecl, kinds []C.Z3_symbol) {
if len(kinds) == 0 {
C.Z3_fixedpoint_set_predicate_representation(f.ctx.ptr, f.ptr, funcDecl.ptr, 0, nil)
return
}
C.Z3_fixedpoint_set_predicate_representation(f.ctx.ptr, f.ptr, funcDecl.ptr, C.uint(len(kinds)), &kinds[0])
}
// FromString parses a Datalog program from a string
func (f *Fixedpoint) FromString(s string) {
cstr := C.CString(s)
defer C.free(unsafe.Pointer(cstr))
C.Z3_fixedpoint_from_string(f.ctx.ptr, f.ptr, cstr)
}
// FromFile parses a Datalog program from a file
func (f *Fixedpoint) FromFile(filename string) {
cstr := C.CString(filename)
defer C.free(unsafe.Pointer(cstr))
C.Z3_fixedpoint_from_file(f.ctx.ptr, f.ptr, cstr)
}
// Statistics represents statistics for Z3 solvers
type Statistics struct {
ctx *Context
ptr C.Z3_stats
}
// newStatistics creates a new Statistics object with proper memory management
func newStatistics(ctx *Context, ptr C.Z3_stats) *Statistics {
stats := &Statistics{ctx: ctx, ptr: ptr}
C.Z3_stats_inc_ref(ctx.ptr, ptr)
runtime.SetFinalizer(stats, func(s *Statistics) {
C.Z3_stats_dec_ref(s.ctx.ptr, s.ptr)
})
return stats
}
// String returns the string representation of statistics
func (s *Statistics) String() string {
cstr := C.Z3_stats_to_string(s.ctx.ptr, s.ptr)
return C.GoString(cstr)
}
// Size returns the number of statistical data entries
func (s *Statistics) Size() int {
return int(C.Z3_stats_size(s.ctx.ptr, s.ptr))
}
// GetKey returns the key (name) of a statistical data entry at the given index
func (s *Statistics) GetKey(idx int) string {
cstr := C.Z3_stats_get_key(s.ctx.ptr, s.ptr, C.uint(idx))
return C.GoString(cstr)
}
// IsUint returns true if the statistical data at the given index is unsigned integer
func (s *Statistics) IsUint(idx int) bool {
return C.Z3_stats_is_uint(s.ctx.ptr, s.ptr, C.uint(idx)) != 0
}
// IsDouble returns true if the statistical data at the given index is double
func (s *Statistics) IsDouble(idx int) bool {
return C.Z3_stats_is_double(s.ctx.ptr, s.ptr, C.uint(idx)) != 0
}
// GetUintValue returns the unsigned integer value at the given index
func (s *Statistics) GetUintValue(idx int) uint64 {
return uint64(C.Z3_stats_get_uint_value(s.ctx.ptr, s.ptr, C.uint(idx)))
}
// GetDoubleValue returns the double value at the given index
func (s *Statistics) GetDoubleValue(idx int) float64 {
return float64(C.Z3_stats_get_double_value(s.ctx.ptr, s.ptr, C.uint(idx)))
}

139
src/api/go/fp.go Normal file
View file

@ -0,0 +1,139 @@
package z3
/*
#include "z3.h"
#include <stdlib.h>
*/
import "C"
import (
"unsafe"
)
// Floating-point operations
// MkFPSort creates a floating-point sort.
func (c *Context) MkFPSort(ebits, sbits uint) *Sort {
return newSort(c, C.Z3_mk_fpa_sort(c.ptr, C.uint(ebits), C.uint(sbits)))
}
// MkFPSort16 creates a 16-bit floating-point sort.
func (c *Context) MkFPSort16() *Sort {
return newSort(c, C.Z3_mk_fpa_sort_16(c.ptr))
}
// MkFPSort32 creates a 32-bit floating-point sort (single precision).
func (c *Context) MkFPSort32() *Sort {
return newSort(c, C.Z3_mk_fpa_sort_32(c.ptr))
}
// MkFPSort64 creates a 64-bit floating-point sort (double precision).
func (c *Context) MkFPSort64() *Sort {
return newSort(c, C.Z3_mk_fpa_sort_64(c.ptr))
}
// MkFPSort128 creates a 128-bit floating-point sort (quadruple precision).
func (c *Context) MkFPSort128() *Sort {
return newSort(c, C.Z3_mk_fpa_sort_128(c.ptr))
}
// MkFPRoundingModeSort creates the rounding mode sort.
func (c *Context) MkFPRoundingModeSort() *Sort {
return newSort(c, C.Z3_mk_fpa_rounding_mode_sort(c.ptr))
}
// MkFPNumeral creates a floating-point numeral from a string.
func (c *Context) MkFPNumeral(value string, sort *Sort) *Expr {
cStr := C.CString(value)
defer C.free(unsafe.Pointer(cStr))
return newExpr(c, C.Z3_mk_numeral(c.ptr, cStr, sort.ptr))
}
// MkFPInf creates a floating-point infinity.
func (c *Context) MkFPInf(sort *Sort, negative bool) *Expr {
return newExpr(c, C.Z3_mk_fpa_inf(c.ptr, sort.ptr, C.bool(negative)))
}
// MkFPNaN creates a floating-point NaN.
func (c *Context) MkFPNaN(sort *Sort) *Expr {
return newExpr(c, C.Z3_mk_fpa_nan(c.ptr, sort.ptr))
}
// MkFPZero creates a floating-point zero.
func (c *Context) MkFPZero(sort *Sort, negative bool) *Expr {
return newExpr(c, C.Z3_mk_fpa_zero(c.ptr, sort.ptr, C.bool(negative)))
}
// MkFPAdd creates a floating-point addition.
func (c *Context) MkFPAdd(rm, lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_fpa_add(c.ptr, rm.ptr, lhs.ptr, rhs.ptr))
}
// MkFPSub creates a floating-point subtraction.
func (c *Context) MkFPSub(rm, lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_fpa_sub(c.ptr, rm.ptr, lhs.ptr, rhs.ptr))
}
// MkFPMul creates a floating-point multiplication.
func (c *Context) MkFPMul(rm, lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_fpa_mul(c.ptr, rm.ptr, lhs.ptr, rhs.ptr))
}
// MkFPDiv creates a floating-point division.
func (c *Context) MkFPDiv(rm, lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_fpa_div(c.ptr, rm.ptr, lhs.ptr, rhs.ptr))
}
// MkFPNeg creates a floating-point negation.
func (c *Context) MkFPNeg(expr *Expr) *Expr {
return newExpr(c, C.Z3_mk_fpa_neg(c.ptr, expr.ptr))
}
// MkFPAbs creates a floating-point absolute value.
func (c *Context) MkFPAbs(expr *Expr) *Expr {
return newExpr(c, C.Z3_mk_fpa_abs(c.ptr, expr.ptr))
}
// MkFPSqrt creates a floating-point square root.
func (c *Context) MkFPSqrt(rm, expr *Expr) *Expr {
return newExpr(c, C.Z3_mk_fpa_sqrt(c.ptr, rm.ptr, expr.ptr))
}
// MkFPLT creates a floating-point less-than.
func (c *Context) MkFPLT(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_fpa_lt(c.ptr, lhs.ptr, rhs.ptr))
}
// MkFPGT creates a floating-point greater-than.
func (c *Context) MkFPGT(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_fpa_gt(c.ptr, lhs.ptr, rhs.ptr))
}
// MkFPLE creates a floating-point less-than-or-equal.
func (c *Context) MkFPLE(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_fpa_leq(c.ptr, lhs.ptr, rhs.ptr))
}
// MkFPGE creates a floating-point greater-than-or-equal.
func (c *Context) MkFPGE(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_fpa_geq(c.ptr, lhs.ptr, rhs.ptr))
}
// MkFPEq creates a floating-point equality.
func (c *Context) MkFPEq(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_fpa_eq(c.ptr, lhs.ptr, rhs.ptr))
}
// MkFPIsNaN creates a predicate checking if a floating-point number is NaN.
func (c *Context) MkFPIsNaN(expr *Expr) *Expr {
return newExpr(c, C.Z3_mk_fpa_is_nan(c.ptr, expr.ptr))
}
// MkFPIsInf creates a predicate checking if a floating-point number is infinite.
func (c *Context) MkFPIsInf(expr *Expr) *Expr {
return newExpr(c, C.Z3_mk_fpa_is_infinite(c.ptr, expr.ptr))
}
// MkFPIsZero creates a predicate checking if a floating-point number is zero.
func (c *Context) MkFPIsZero(expr *Expr) *Expr {
return newExpr(c, C.Z3_mk_fpa_is_zero(c.ptr, expr.ptr))
}

6
src/api/go/go.mod Normal file
View file

@ -0,0 +1,6 @@
module github.com/Z3Prover/z3/src/api/go
go 1.20
// This package provides Go bindings for the Z3 theorem prover.
// It uses CGO to wrap the Z3 C API.

67
src/api/go/log.go Normal file
View file

@ -0,0 +1,67 @@
// Copyright (c) Microsoft Corporation 2025
// Z3 Go API: Logging functionality
package z3
/*
#include "z3.h"
#include <stdlib.h>
*/
import "C"
import (
"sync"
"unsafe"
)
var (
logMutex sync.Mutex
isLogOpen bool
)
// OpenLog opens an interaction log file
// Returns true if successful, false otherwise
func OpenLog(filename string) bool {
logMutex.Lock()
defer logMutex.Unlock()
cFilename := C.CString(filename)
defer C.free(unsafe.Pointer(cFilename))
result := C.Z3_open_log(cFilename)
if result != 0 {
isLogOpen = true
return true
}
return false
}
// CloseLog closes the interaction log
func CloseLog() {
logMutex.Lock()
defer logMutex.Unlock()
C.Z3_close_log()
isLogOpen = false
}
// AppendLog appends a user-provided string to the interaction log
// Panics if the log is not open
func AppendLog(s string) {
logMutex.Lock()
defer logMutex.Unlock()
if !isLogOpen {
panic("Log is not open")
}
cStr := C.CString(s)
defer C.free(unsafe.Pointer(cStr))
C.Z3_append_log(cStr)
}
// IsLogOpen returns true if the interaction log is open
func IsLogOpen() bool {
logMutex.Lock()
defer logMutex.Unlock()
return isLogOpen
}

218
src/api/go/optimize.go Normal file
View file

@ -0,0 +1,218 @@
package z3
/*
#include "z3.h"
#include <stdlib.h>
*/
import "C"
import (
"runtime"
"unsafe"
)
// Optimize represents a Z3 optimization context for solving optimization problems.
// Unlike Solver which only checks satisfiability, Optimize can find optimal solutions
// with respect to objective functions.
type Optimize struct {
ctx *Context
ptr C.Z3_optimize
}
// NewOptimize creates a new optimization context.
func (c *Context) NewOptimize() *Optimize {
opt := &Optimize{
ctx: c,
ptr: C.Z3_mk_optimize(c.ptr),
}
C.Z3_optimize_inc_ref(c.ptr, opt.ptr)
runtime.SetFinalizer(opt, func(o *Optimize) {
C.Z3_optimize_dec_ref(o.ctx.ptr, o.ptr)
})
return opt
}
// String returns the string representation of the optimize context.
func (o *Optimize) String() string {
return C.GoString(C.Z3_optimize_to_string(o.ctx.ptr, o.ptr))
}
// Assert adds a constraint to the optimizer.
func (o *Optimize) Assert(constraint *Expr) {
C.Z3_optimize_assert(o.ctx.ptr, o.ptr, constraint.ptr)
}
// AssertAndTrack adds a constraint with a tracking literal for unsat core extraction.
func (o *Optimize) AssertAndTrack(constraint, track *Expr) {
C.Z3_optimize_assert_and_track(o.ctx.ptr, o.ptr, constraint.ptr, track.ptr)
}
// AssertSoft adds a soft constraint with a weight.
// Soft constraints are used for MaxSMT problems.
// Returns a handle to the objective.
func (o *Optimize) AssertSoft(constraint *Expr, weight string, group string) uint {
cWeight := C.CString(weight)
cGroup := C.CString(group)
defer C.free(unsafe.Pointer(cWeight))
defer C.free(unsafe.Pointer(cGroup))
sym := o.ctx.MkStringSymbol(group)
return uint(C.Z3_optimize_assert_soft(o.ctx.ptr, o.ptr, constraint.ptr, cWeight, sym.ptr))
}
// Maximize adds a maximization objective.
// Returns a handle to the objective that can be used to retrieve bounds.
func (o *Optimize) Maximize(expr *Expr) uint {
return uint(C.Z3_optimize_maximize(o.ctx.ptr, o.ptr, expr.ptr))
}
// Minimize adds a minimization objective.
// Returns a handle to the objective that can be used to retrieve bounds.
func (o *Optimize) Minimize(expr *Expr) uint {
return uint(C.Z3_optimize_minimize(o.ctx.ptr, o.ptr, expr.ptr))
}
// Check checks the satisfiability of the constraints and optimizes objectives.
func (o *Optimize) Check(assumptions ...*Expr) Status {
var result C.Z3_lbool
if len(assumptions) == 0 {
result = C.Z3_optimize_check(o.ctx.ptr, o.ptr, 0, nil)
} else {
cAssumptions := make([]C.Z3_ast, len(assumptions))
for i, a := range assumptions {
cAssumptions[i] = a.ptr
}
result = C.Z3_optimize_check(o.ctx.ptr, o.ptr, C.uint(len(assumptions)), &cAssumptions[0])
}
return Status(result)
}
// Model returns the model if the constraints are satisfiable.
func (o *Optimize) Model() *Model {
modelPtr := C.Z3_optimize_get_model(o.ctx.ptr, o.ptr)
if modelPtr == nil {
return nil
}
return newModel(o.ctx, modelPtr)
}
// Push creates a backtracking point.
func (o *Optimize) Push() {
C.Z3_optimize_push(o.ctx.ptr, o.ptr)
}
// Pop removes a backtracking point.
func (o *Optimize) Pop() {
C.Z3_optimize_pop(o.ctx.ptr, o.ptr)
}
// GetLower retrieves a lower bound for the objective at the given index.
func (o *Optimize) GetLower(index uint) *Expr {
result := C.Z3_optimize_get_lower(o.ctx.ptr, o.ptr, C.uint(index))
if result == nil {
return nil
}
return newExpr(o.ctx, result)
}
// GetUpper retrieves an upper bound for the objective at the given index.
func (o *Optimize) GetUpper(index uint) *Expr {
result := C.Z3_optimize_get_upper(o.ctx.ptr, o.ptr, C.uint(index))
if result == nil {
return nil
}
return newExpr(o.ctx, result)
}
// GetLowerAsVector retrieves a lower bound as a vector (inf, value, eps).
// The objective value is unbounded if inf is non-zero,
// otherwise it's represented as value + eps * EPSILON.
func (o *Optimize) GetLowerAsVector(index uint) []*Expr {
vec := C.Z3_optimize_get_lower_as_vector(o.ctx.ptr, o.ptr, C.uint(index))
size := uint(C.Z3_ast_vector_size(o.ctx.ptr, vec))
if size != 3 {
return nil
}
return []*Expr{
newExpr(o.ctx, C.Z3_ast_vector_get(o.ctx.ptr, vec, 0)),
newExpr(o.ctx, C.Z3_ast_vector_get(o.ctx.ptr, vec, 1)),
newExpr(o.ctx, C.Z3_ast_vector_get(o.ctx.ptr, vec, 2)),
}
}
// GetUpperAsVector retrieves an upper bound as a vector (inf, value, eps).
// The objective value is unbounded if inf is non-zero,
// otherwise it's represented as value + eps * EPSILON.
func (o *Optimize) GetUpperAsVector(index uint) []*Expr {
vec := C.Z3_optimize_get_upper_as_vector(o.ctx.ptr, o.ptr, C.uint(index))
size := uint(C.Z3_ast_vector_size(o.ctx.ptr, vec))
if size != 3 {
return nil
}
return []*Expr{
newExpr(o.ctx, C.Z3_ast_vector_get(o.ctx.ptr, vec, 0)),
newExpr(o.ctx, C.Z3_ast_vector_get(o.ctx.ptr, vec, 1)),
newExpr(o.ctx, C.Z3_ast_vector_get(o.ctx.ptr, vec, 2)),
}
}
// ReasonUnknown returns the reason why the result is unknown.
func (o *Optimize) ReasonUnknown() string {
return C.GoString(C.Z3_optimize_get_reason_unknown(o.ctx.ptr, o.ptr))
}
// GetHelp returns help information for the optimizer.
func (o *Optimize) GetHelp() string {
return C.GoString(C.Z3_optimize_get_help(o.ctx.ptr, o.ptr))
}
// SetParams sets parameters for the optimizer.
func (o *Optimize) SetParams(params *Params) {
C.Z3_optimize_set_params(o.ctx.ptr, o.ptr, params.ptr)
}
// Assertions returns the assertions in the optimizer.
func (o *Optimize) Assertions() []*Expr {
vec := C.Z3_optimize_get_assertions(o.ctx.ptr, o.ptr)
size := uint(C.Z3_ast_vector_size(o.ctx.ptr, vec))
result := make([]*Expr, size)
for i := uint(0); i < size; i++ {
result[i] = newExpr(o.ctx, C.Z3_ast_vector_get(o.ctx.ptr, vec, C.uint(i)))
}
return result
}
// Objectives returns the objectives in the optimizer.
func (o *Optimize) Objectives() []*Expr {
vec := C.Z3_optimize_get_objectives(o.ctx.ptr, o.ptr)
size := uint(C.Z3_ast_vector_size(o.ctx.ptr, vec))
result := make([]*Expr, size)
for i := uint(0); i < size; i++ {
result[i] = newExpr(o.ctx, C.Z3_ast_vector_get(o.ctx.ptr, vec, C.uint(i)))
}
return result
}
// UnsatCore returns the unsat core if the constraints are unsatisfiable.
func (o *Optimize) UnsatCore() []*Expr {
vec := C.Z3_optimize_get_unsat_core(o.ctx.ptr, o.ptr)
size := uint(C.Z3_ast_vector_size(o.ctx.ptr, vec))
result := make([]*Expr, size)
for i := uint(0); i < size; i++ {
result[i] = newExpr(o.ctx, C.Z3_ast_vector_get(o.ctx.ptr, vec, C.uint(i)))
}
return result
}
// FromFile parses an SMT-LIB2 file with optimization objectives and constraints.
func (o *Optimize) FromFile(filename string) {
cFilename := C.CString(filename)
defer C.free(unsafe.Pointer(cFilename))
C.Z3_optimize_from_file(o.ctx.ptr, o.ptr, cFilename)
}
// FromString parses an SMT-LIB2 string with optimization objectives and constraints.
func (o *Optimize) FromString(s string) {
cStr := C.CString(s)
defer C.free(unsafe.Pointer(cStr))
C.Z3_optimize_from_string(o.ctx.ptr, o.ptr, cStr)
}

232
src/api/go/seq.go Normal file
View file

@ -0,0 +1,232 @@
package z3
/*
#include "z3.h"
#include <stdlib.h>
*/
import "C"
import (
"unsafe"
)
// Sequence and string operations
// MkSeqSort creates a sequence sort.
func (c *Context) MkSeqSort(elemSort *Sort) *Sort {
return newSort(c, C.Z3_mk_seq_sort(c.ptr, elemSort.ptr))
}
// MkStringSort creates a string sort (sequence of characters).
func (c *Context) MkStringSort() *Sort {
return newSort(c, C.Z3_mk_string_sort(c.ptr))
}
// MkString creates a string constant.
func (c *Context) MkString(value string) *Expr {
cStr := C.CString(value)
defer C.free(unsafe.Pointer(cStr))
return newExpr(c, C.Z3_mk_string(c.ptr, cStr))
}
// MkEmptySeq creates an empty sequence.
func (c *Context) MkEmptySeq(sort *Sort) *Expr {
return newExpr(c, C.Z3_mk_seq_empty(c.ptr, sort.ptr))
}
// MkSeqUnit creates a unit sequence containing a single element.
func (c *Context) MkSeqUnit(elem *Expr) *Expr {
return newExpr(c, C.Z3_mk_seq_unit(c.ptr, elem.ptr))
}
// MkSeqConcat creates a sequence concatenation.
func (c *Context) MkSeqConcat(exprs ...*Expr) *Expr {
if len(exprs) == 0 {
return nil
}
if len(exprs) == 1 {
return exprs[0]
}
cExprs := make([]C.Z3_ast, len(exprs))
for i, e := range exprs {
cExprs[i] = e.ptr
}
return newExpr(c, C.Z3_mk_seq_concat(c.ptr, C.uint(len(exprs)), &cExprs[0]))
}
// MkSeqLength creates a sequence length operation.
func (c *Context) MkSeqLength(seq *Expr) *Expr {
return newExpr(c, C.Z3_mk_seq_length(c.ptr, seq.ptr))
}
// MkSeqPrefix creates a sequence prefix predicate.
func (c *Context) MkSeqPrefix(prefix, seq *Expr) *Expr {
return newExpr(c, C.Z3_mk_seq_prefix(c.ptr, prefix.ptr, seq.ptr))
}
// MkSeqSuffix creates a sequence suffix predicate.
func (c *Context) MkSeqSuffix(suffix, seq *Expr) *Expr {
return newExpr(c, C.Z3_mk_seq_suffix(c.ptr, suffix.ptr, seq.ptr))
}
// MkSeqContains creates a sequence contains predicate.
func (c *Context) MkSeqContains(seq, substr *Expr) *Expr {
return newExpr(c, C.Z3_mk_seq_contains(c.ptr, seq.ptr, substr.ptr))
}
// MkSeqAt creates a sequence element access operation.
func (c *Context) MkSeqAt(seq, index *Expr) *Expr {
return newExpr(c, C.Z3_mk_seq_at(c.ptr, seq.ptr, index.ptr))
}
// MkSeqExtract creates a sequence extract (substring) operation.
func (c *Context) MkSeqExtract(seq, offset, length *Expr) *Expr {
return newExpr(c, C.Z3_mk_seq_extract(c.ptr, seq.ptr, offset.ptr, length.ptr))
}
// MkSeqReplace creates a sequence replace operation.
func (c *Context) MkSeqReplace(seq, src, dst *Expr) *Expr {
return newExpr(c, C.Z3_mk_seq_replace(c.ptr, seq.ptr, src.ptr, dst.ptr))
}
// MkSeqIndexOf creates a sequence index-of operation.
func (c *Context) MkSeqIndexOf(seq, substr, offset *Expr) *Expr {
return newExpr(c, C.Z3_mk_seq_index(c.ptr, seq.ptr, substr.ptr, offset.ptr))
}
// MkStrToInt creates a string-to-integer conversion.
func (c *Context) MkStrToInt(str *Expr) *Expr {
return newExpr(c, C.Z3_mk_str_to_int(c.ptr, str.ptr))
}
// MkIntToStr creates an integer-to-string conversion.
func (c *Context) MkIntToStr(num *Expr) *Expr {
return newExpr(c, C.Z3_mk_int_to_str(c.ptr, num.ptr))
}
// Regular expression operations
// MkReSort creates a regular expression sort.
func (c *Context) MkReSort(seqSort *Sort) *Sort {
return newSort(c, C.Z3_mk_re_sort(c.ptr, seqSort.ptr))
}
// MkToRe converts a sequence to a regular expression that accepts exactly that sequence.
func (c *Context) MkToRe(seq *Expr) *Expr {
return newExpr(c, C.Z3_mk_seq_to_re(c.ptr, seq.ptr))
}
// MkInRe creates a membership predicate for a sequence in a regular expression.
func (c *Context) MkInRe(seq, re *Expr) *Expr {
return newExpr(c, C.Z3_mk_seq_in_re(c.ptr, seq.ptr, re.ptr))
}
// MkReStar creates a Kleene star (zero or more repetitions) of a regular expression.
func (c *Context) MkReStar(re *Expr) *Expr {
return newExpr(c, C.Z3_mk_re_star(c.ptr, re.ptr))
}
// MkRePlus creates a Kleene plus (one or more repetitions) of a regular expression.
func (c *Context) MkRePlus(re *Expr) *Expr {
return newExpr(c, C.Z3_mk_re_plus(c.ptr, re.ptr))
}
// MkReOption creates an optional regular expression (zero or one repetition).
func (c *Context) MkReOption(re *Expr) *Expr {
return newExpr(c, C.Z3_mk_re_option(c.ptr, re.ptr))
}
// MkRePower creates a regular expression that matches exactly n repetitions.
func (c *Context) MkRePower(re *Expr, n uint) *Expr {
return newExpr(c, C.Z3_mk_re_power(c.ptr, re.ptr, C.uint(n)))
}
// MkReLoop creates a regular expression with bounded repetition (between lo and hi times).
// If hi is 0, it means unbounded (at least lo times).
func (c *Context) MkReLoop(re *Expr, lo, hi uint) *Expr {
return newExpr(c, C.Z3_mk_re_loop(c.ptr, re.ptr, C.uint(lo), C.uint(hi)))
}
// MkReConcat creates a concatenation of regular expressions.
func (c *Context) MkReConcat(regexes ...*Expr) *Expr {
if len(regexes) == 0 {
return nil
}
if len(regexes) == 1 {
return regexes[0]
}
cRegexes := make([]C.Z3_ast, len(regexes))
for i, re := range regexes {
cRegexes[i] = re.ptr
}
return newExpr(c, C.Z3_mk_re_concat(c.ptr, C.uint(len(regexes)), &cRegexes[0]))
}
// MkReUnion creates a union (alternation) of regular expressions.
func (c *Context) MkReUnion(regexes ...*Expr) *Expr {
if len(regexes) == 0 {
return nil
}
if len(regexes) == 1 {
return regexes[0]
}
cRegexes := make([]C.Z3_ast, len(regexes))
for i, re := range regexes {
cRegexes[i] = re.ptr
}
return newExpr(c, C.Z3_mk_re_union(c.ptr, C.uint(len(regexes)), &cRegexes[0]))
}
// MkReIntersect creates an intersection of regular expressions.
func (c *Context) MkReIntersect(regexes ...*Expr) *Expr {
if len(regexes) == 0 {
return nil
}
if len(regexes) == 1 {
return regexes[0]
}
cRegexes := make([]C.Z3_ast, len(regexes))
for i, re := range regexes {
cRegexes[i] = re.ptr
}
return newExpr(c, C.Z3_mk_re_intersect(c.ptr, C.uint(len(regexes)), &cRegexes[0]))
}
// MkReComplement creates the complement of a regular expression.
func (c *Context) MkReComplement(re *Expr) *Expr {
return newExpr(c, C.Z3_mk_re_complement(c.ptr, re.ptr))
}
// MkReDiff creates the difference of two regular expressions (a - b).
func (c *Context) MkReDiff(a, b *Expr) *Expr {
return newExpr(c, C.Z3_mk_re_diff(c.ptr, a.ptr, b.ptr))
}
// MkReEmpty creates an empty regular expression (accepts no strings).
func (c *Context) MkReEmpty(sort *Sort) *Expr {
return newExpr(c, C.Z3_mk_re_empty(c.ptr, sort.ptr))
}
// MkReFull creates a full regular expression (accepts all strings).
func (c *Context) MkReFull(sort *Sort) *Expr {
return newExpr(c, C.Z3_mk_re_full(c.ptr, sort.ptr))
}
// MkReAllchar creates a regular expression that accepts all single characters.
func (c *Context) MkReAllchar(sort *Sort) *Expr {
return newExpr(c, C.Z3_mk_re_allchar(c.ptr, sort.ptr))
}
// MkReRange creates a regular expression for a character range [lo, hi].
func (c *Context) MkReRange(lo, hi *Expr) *Expr {
return newExpr(c, C.Z3_mk_re_range(c.ptr, lo.ptr, hi.ptr))
}
// MkSeqReplaceRe replaces the first occurrence matching a regular expression.
func (c *Context) MkSeqReplaceRe(seq, re, replacement *Expr) *Expr {
return newExpr(c, C.Z3_mk_seq_replace_re(c.ptr, seq.ptr, re.ptr, replacement.ptr))
}
// MkSeqReplaceReAll replaces all occurrences matching a regular expression.
func (c *Context) MkSeqReplaceReAll(seq, re, replacement *Expr) *Expr {
return newExpr(c, C.Z3_mk_seq_replace_re_all(c.ptr, seq.ptr, re.ptr, replacement.ptr))
}

263
src/api/go/solver.go Normal file
View file

@ -0,0 +1,263 @@
package z3
/*
#include "z3.h"
#include <stdlib.h>
*/
import "C"
import (
"runtime"
"unsafe"
)
// Status represents the result of a satisfiability check.
type Status int
const (
// Unsatisfiable means the constraints are unsatisfiable.
Unsatisfiable Status = -1
// Unknown means Z3 could not determine satisfiability.
Unknown Status = 0
// Satisfiable means the constraints are satisfiable.
Satisfiable Status = 1
)
// String returns the string representation of the status.
func (s Status) String() string {
switch s {
case Unsatisfiable:
return "unsat"
case Unknown:
return "unknown"
case Satisfiable:
return "sat"
default:
return "unknown"
}
}
// Solver represents a Z3 solver.
type Solver struct {
ctx *Context
ptr C.Z3_solver
}
// NewSolver creates a new solver for the given context.
func (c *Context) NewSolver() *Solver {
s := &Solver{
ctx: c,
ptr: C.Z3_mk_solver(c.ptr),
}
C.Z3_solver_inc_ref(c.ptr, s.ptr)
runtime.SetFinalizer(s, func(solver *Solver) {
C.Z3_solver_dec_ref(solver.ctx.ptr, solver.ptr)
})
return s
}
// NewSolverForLogic creates a solver for a specific logic.
func (c *Context) NewSolverForLogic(logic string) *Solver {
sym := c.MkStringSymbol(logic)
s := &Solver{
ctx: c,
ptr: C.Z3_mk_solver_for_logic(c.ptr, sym.ptr),
}
C.Z3_solver_inc_ref(c.ptr, s.ptr)
runtime.SetFinalizer(s, func(solver *Solver) {
C.Z3_solver_dec_ref(solver.ctx.ptr, solver.ptr)
})
return s
}
// String returns the string representation of the solver.
func (s *Solver) String() string {
return C.GoString(C.Z3_solver_to_string(s.ctx.ptr, s.ptr))
}
// Assert adds a constraint to the solver.
func (s *Solver) Assert(constraint *Expr) {
C.Z3_solver_assert(s.ctx.ptr, s.ptr, constraint.ptr)
}
// AssertAndTrack adds a constraint with a tracking literal.
func (s *Solver) AssertAndTrack(constraint, track *Expr) {
C.Z3_solver_assert_and_track(s.ctx.ptr, s.ptr, constraint.ptr, track.ptr)
}
// Check checks the satisfiability of the constraints.
func (s *Solver) Check() Status {
result := C.Z3_solver_check(s.ctx.ptr, s.ptr)
return Status(result)
}
// CheckAssumptions checks satisfiability under assumptions.
func (s *Solver) CheckAssumptions(assumptions ...*Expr) Status {
if len(assumptions) == 0 {
return s.Check()
}
cAssumptions := make([]C.Z3_ast, len(assumptions))
for i, a := range assumptions {
cAssumptions[i] = a.ptr
}
result := C.Z3_solver_check_assumptions(s.ctx.ptr, s.ptr, C.uint(len(assumptions)), &cAssumptions[0])
return Status(result)
}
// Model returns the model if the constraints are satisfiable.
func (s *Solver) Model() *Model {
modelPtr := C.Z3_solver_get_model(s.ctx.ptr, s.ptr)
if modelPtr == nil {
return nil
}
return newModel(s.ctx, modelPtr)
}
// Push creates a backtracking point.
func (s *Solver) Push() {
C.Z3_solver_push(s.ctx.ptr, s.ptr)
}
// Pop removes backtracking points.
func (s *Solver) Pop(n uint) {
C.Z3_solver_pop(s.ctx.ptr, s.ptr, C.uint(n))
}
// Reset removes all assertions from the solver.
func (s *Solver) Reset() {
C.Z3_solver_reset(s.ctx.ptr, s.ptr)
}
// NumScopes returns the number of backtracking points.
func (s *Solver) NumScopes() uint {
return uint(C.Z3_solver_get_num_scopes(s.ctx.ptr, s.ptr))
}
// Assertions returns the assertions in the solver.
func (s *Solver) Assertions() []*Expr {
vec := C.Z3_solver_get_assertions(s.ctx.ptr, s.ptr)
size := uint(C.Z3_ast_vector_size(s.ctx.ptr, vec))
result := make([]*Expr, size)
for i := uint(0); i < size; i++ {
result[i] = newExpr(s.ctx, C.Z3_ast_vector_get(s.ctx.ptr, vec, C.uint(i)))
}
return result
}
// UnsatCore returns the unsat core if the constraints are unsatisfiable.
func (s *Solver) UnsatCore() []*Expr {
vec := C.Z3_solver_get_unsat_core(s.ctx.ptr, s.ptr)
size := uint(C.Z3_ast_vector_size(s.ctx.ptr, vec))
result := make([]*Expr, size)
for i := uint(0); i < size; i++ {
result[i] = newExpr(s.ctx, C.Z3_ast_vector_get(s.ctx.ptr, vec, C.uint(i)))
}
return result
}
// ReasonUnknown returns the reason why the result is unknown.
func (s *Solver) ReasonUnknown() string {
return C.GoString(C.Z3_solver_get_reason_unknown(s.ctx.ptr, s.ptr))
}
// Model represents a Z3 model (satisfying assignment).
type Model struct {
ctx *Context
ptr C.Z3_model
}
// newModel creates a new Model and manages its reference count.
func newModel(ctx *Context, ptr C.Z3_model) *Model {
m := &Model{ctx: ctx, ptr: ptr}
C.Z3_model_inc_ref(ctx.ptr, ptr)
runtime.SetFinalizer(m, func(model *Model) {
C.Z3_model_dec_ref(model.ctx.ptr, model.ptr)
})
return m
}
// String returns the string representation of the model.
func (m *Model) String() string {
return C.GoString(C.Z3_model_to_string(m.ctx.ptr, m.ptr))
}
// NumConsts returns the number of constants in the model.
func (m *Model) NumConsts() uint {
return uint(C.Z3_model_get_num_consts(m.ctx.ptr, m.ptr))
}
// NumFuncs returns the number of function interpretations in the model.
func (m *Model) NumFuncs() uint {
return uint(C.Z3_model_get_num_funcs(m.ctx.ptr, m.ptr))
}
// GetConstDecl returns the i-th constant declaration in the model.
func (m *Model) GetConstDecl(i uint) *FuncDecl {
return newFuncDecl(m.ctx, C.Z3_model_get_const_decl(m.ctx.ptr, m.ptr, C.uint(i)))
}
// GetFuncDecl returns the i-th function declaration in the model.
func (m *Model) GetFuncDecl(i uint) *FuncDecl {
return newFuncDecl(m.ctx, C.Z3_model_get_func_decl(m.ctx.ptr, m.ptr, C.uint(i)))
}
// Eval evaluates an expression in the model.
// If modelCompletion is true, Z3 will assign an interpretation for uninterpreted constants.
func (m *Model) Eval(expr *Expr, modelCompletion bool) (*Expr, bool) {
var result C.Z3_ast
var completion C.bool
if modelCompletion {
completion = C.bool(true)
} else {
completion = C.bool(false)
}
success := C.Z3_model_eval(m.ctx.ptr, m.ptr, expr.ptr, completion, &result)
if success == C.bool(false) {
return nil, false
}
return newExpr(m.ctx, result), true
}
// GetConstInterp returns the interpretation of a constant.
func (m *Model) GetConstInterp(decl *FuncDecl) *Expr {
result := C.Z3_model_get_const_interp(m.ctx.ptr, m.ptr, decl.ptr)
if result == nil {
return nil
}
return newExpr(m.ctx, result)
}
// FuncInterp represents a function interpretation in a model.
type FuncInterp struct {
ctx *Context
ptr C.Z3_func_interp
}
// GetFuncInterp returns the interpretation of a function.
func (m *Model) GetFuncInterp(decl *FuncDecl) *FuncInterp {
result := C.Z3_model_get_func_interp(m.ctx.ptr, m.ptr, decl.ptr)
if result == nil {
return nil
}
fi := &FuncInterp{ctx: m.ctx, ptr: result}
C.Z3_func_interp_inc_ref(m.ctx.ptr, result)
runtime.SetFinalizer(fi, func(f *FuncInterp) {
C.Z3_func_interp_dec_ref(f.ctx.ptr, f.ptr)
})
return fi
}
// NumEntries returns the number of entries in the function interpretation.
func (fi *FuncInterp) NumEntries() uint {
return uint(C.Z3_func_interp_get_num_entries(fi.ctx.ptr, fi.ptr))
}
// GetElse returns the else value of the function interpretation.
func (fi *FuncInterp) GetElse() *Expr {
result := C.Z3_func_interp_get_else(fi.ctx.ptr, fi.ptr)
return newExpr(fi.ctx, result)
}
// GetArity returns the arity of the function interpretation.
func (fi *FuncInterp) GetArity() uint {
return uint(C.Z3_func_interp_get_arity(fi.ctx.ptr, fi.ptr))
}

294
src/api/go/tactic.go Normal file
View file

@ -0,0 +1,294 @@
package z3
/*
#include "z3.h"
#include <stdlib.h>
*/
import "C"
import (
"runtime"
"unsafe"
)
// Tactic represents a Z3 tactic for transforming goals.
type Tactic struct {
ctx *Context
ptr C.Z3_tactic
}
// newTactic creates a new Tactic and manages its reference count.
func newTactic(ctx *Context, ptr C.Z3_tactic) *Tactic {
t := &Tactic{ctx: ctx, ptr: ptr}
C.Z3_tactic_inc_ref(ctx.ptr, ptr)
runtime.SetFinalizer(t, func(tactic *Tactic) {
C.Z3_tactic_dec_ref(tactic.ctx.ptr, tactic.ptr)
})
return t
}
// MkTactic creates a tactic with the given name.
func (c *Context) MkTactic(name string) *Tactic {
cName := C.CString(name)
defer C.free(unsafe.Pointer(cName))
return newTactic(c, C.Z3_mk_tactic(c.ptr, cName))
}
// Apply applies the tactic to a goal.
func (t *Tactic) Apply(g *Goal) *ApplyResult {
return newApplyResult(t.ctx, C.Z3_tactic_apply(t.ctx.ptr, t.ptr, g.ptr))
}
// GetHelp returns help information for the tactic.
func (t *Tactic) GetHelp() string {
return C.GoString(C.Z3_tactic_get_help(t.ctx.ptr, t.ptr))
}
// AndThen creates a tactic that applies t1 and then t2.
func (t *Tactic) AndThen(t2 *Tactic) *Tactic {
return newTactic(t.ctx, C.Z3_tactic_and_then(t.ctx.ptr, t.ptr, t2.ptr))
}
// OrElse creates a tactic that applies t1, and if it fails, applies t2.
func (t *Tactic) OrElse(t2 *Tactic) *Tactic {
return newTactic(t.ctx, C.Z3_tactic_or_else(t.ctx.ptr, t.ptr, t2.ptr))
}
// Repeat creates a tactic that applies t repeatedly (at most max times).
func (t *Tactic) Repeat(max uint) *Tactic {
return newTactic(t.ctx, C.Z3_tactic_repeat(t.ctx.ptr, t.ptr, C.uint(max)))
}
// When creates a conditional tactic that applies t only if probe p evaluates to true.
func (c *Context) TacticWhen(p *Probe, t *Tactic) *Tactic {
return newTactic(c, C.Z3_tactic_when(c.ptr, p.ptr, t.ptr))
}
// TacticCond creates a conditional tactic: if p then t1 else t2.
func (c *Context) TacticCond(p *Probe, t1, t2 *Tactic) *Tactic {
return newTactic(c, C.Z3_tactic_cond(c.ptr, p.ptr, t1.ptr, t2.ptr))
}
// TacticFail creates a tactic that always fails.
func (c *Context) TacticFail() *Tactic {
return newTactic(c, C.Z3_tactic_fail(c.ptr))
}
// TacticSkip creates a tactic that always succeeds.
func (c *Context) TacticSkip() *Tactic {
return newTactic(c, C.Z3_tactic_skip(c.ptr))
}
// Goal represents a set of formulas that can be solved or transformed.
type Goal struct {
ctx *Context
ptr C.Z3_goal
}
// newGoal creates a new Goal and manages its reference count.
func newGoal(ctx *Context, ptr C.Z3_goal) *Goal {
g := &Goal{ctx: ctx, ptr: ptr}
C.Z3_goal_inc_ref(ctx.ptr, ptr)
runtime.SetFinalizer(g, func(goal *Goal) {
C.Z3_goal_dec_ref(goal.ctx.ptr, goal.ptr)
})
return g
}
// MkGoal creates a new goal.
func (c *Context) MkGoal(models, unsatCores, proofs bool) *Goal {
return newGoal(c, C.Z3_mk_goal(c.ptr, C.bool(models), C.bool(unsatCores), C.bool(proofs)))
}
// Assert adds a constraint to the goal.
func (g *Goal) Assert(constraint *Expr) {
C.Z3_goal_assert(g.ctx.ptr, g.ptr, constraint.ptr)
}
// Size returns the number of formulas in the goal.
func (g *Goal) Size() uint {
return uint(C.Z3_goal_size(g.ctx.ptr, g.ptr))
}
// Formula returns the i-th formula in the goal.
func (g *Goal) Formula(i uint) *Expr {
return newExpr(g.ctx, C.Z3_goal_formula(g.ctx.ptr, g.ptr, C.uint(i)))
}
// NumExprs returns the number of expressions in the goal.
func (g *Goal) NumExprs() uint {
return uint(C.Z3_goal_num_exprs(g.ctx.ptr, g.ptr))
}
// IsDecidedSat returns true if the goal is decided to be satisfiable.
func (g *Goal) IsDecidedSat() bool {
return bool(C.Z3_goal_is_decided_sat(g.ctx.ptr, g.ptr))
}
// IsDecidedUnsat returns true if the goal is decided to be unsatisfiable.
func (g *Goal) IsDecidedUnsat() bool {
return bool(C.Z3_goal_is_decided_unsat(g.ctx.ptr, g.ptr))
}
// Reset removes all formulas from the goal.
func (g *Goal) Reset() {
C.Z3_goal_reset(g.ctx.ptr, g.ptr)
}
// String returns the string representation of the goal.
func (g *Goal) String() string {
return C.GoString(C.Z3_goal_to_string(g.ctx.ptr, g.ptr))
}
// ApplyResult represents the result of applying a tactic to a goal.
type ApplyResult struct {
ctx *Context
ptr C.Z3_apply_result
}
// newApplyResult creates a new ApplyResult and manages its reference count.
func newApplyResult(ctx *Context, ptr C.Z3_apply_result) *ApplyResult {
ar := &ApplyResult{ctx: ctx, ptr: ptr}
C.Z3_apply_result_inc_ref(ctx.ptr, ptr)
runtime.SetFinalizer(ar, func(result *ApplyResult) {
C.Z3_apply_result_dec_ref(result.ctx.ptr, result.ptr)
})
return ar
}
// NumSubgoals returns the number of subgoals in the result.
func (ar *ApplyResult) NumSubgoals() uint {
return uint(C.Z3_apply_result_get_num_subgoals(ar.ctx.ptr, ar.ptr))
}
// Subgoal returns the i-th subgoal.
func (ar *ApplyResult) Subgoal(i uint) *Goal {
return newGoal(ar.ctx, C.Z3_apply_result_get_subgoal(ar.ctx.ptr, ar.ptr, C.uint(i)))
}
// String returns the string representation of the apply result.
func (ar *ApplyResult) String() string {
return C.GoString(C.Z3_apply_result_to_string(ar.ctx.ptr, ar.ptr))
}
// Probe represents a probe for checking properties of goals.
type Probe struct {
ctx *Context
ptr C.Z3_probe
}
// newProbe creates a new Probe and manages its reference count.
func newProbe(ctx *Context, ptr C.Z3_probe) *Probe {
p := &Probe{ctx: ctx, ptr: ptr}
C.Z3_probe_inc_ref(ctx.ptr, ptr)
runtime.SetFinalizer(p, func(probe *Probe) {
C.Z3_probe_dec_ref(probe.ctx.ptr, probe.ptr)
})
return p
}
// MkProbe creates a probe with the given name.
func (c *Context) MkProbe(name string) *Probe {
cName := C.CString(name)
defer C.free(unsafe.Pointer(cName))
return newProbe(c, C.Z3_mk_probe(c.ptr, cName))
}
// Apply evaluates the probe on a goal.
func (p *Probe) Apply(g *Goal) float64 {
return float64(C.Z3_probe_apply(p.ctx.ptr, p.ptr, g.ptr))
}
// ProbeConst creates a probe that always evaluates to the given value.
func (c *Context) ProbeConst(val float64) *Probe {
return newProbe(c, C.Z3_probe_const(c.ptr, C.double(val)))
}
// ProbeLt creates a probe that evaluates to true if p1 < p2.
func (p *Probe) Lt(p2 *Probe) *Probe {
return newProbe(p.ctx, C.Z3_probe_lt(p.ctx.ptr, p.ptr, p2.ptr))
}
// ProbeGt creates a probe that evaluates to true if p1 > p2.
func (p *Probe) Gt(p2 *Probe) *Probe {
return newProbe(p.ctx, C.Z3_probe_gt(p.ctx.ptr, p.ptr, p2.ptr))
}
// ProbeLe creates a probe that evaluates to true if p1 <= p2.
func (p *Probe) Le(p2 *Probe) *Probe {
return newProbe(p.ctx, C.Z3_probe_le(p.ctx.ptr, p.ptr, p2.ptr))
}
// ProbeGe creates a probe that evaluates to true if p1 >= p2.
func (p *Probe) Ge(p2 *Probe) *Probe {
return newProbe(p.ctx, C.Z3_probe_ge(p.ctx.ptr, p.ptr, p2.ptr))
}
// ProbeEq creates a probe that evaluates to true if p1 == p2.
func (p *Probe) Eq(p2 *Probe) *Probe {
return newProbe(p.ctx, C.Z3_probe_eq(p.ctx.ptr, p.ptr, p2.ptr))
}
// ProbeAnd creates a probe that is the conjunction of p1 and p2.
func (p *Probe) And(p2 *Probe) *Probe {
return newProbe(p.ctx, C.Z3_probe_and(p.ctx.ptr, p.ptr, p2.ptr))
}
// ProbeOr creates a probe that is the disjunction of p1 and p2.
func (p *Probe) Or(p2 *Probe) *Probe {
return newProbe(p.ctx, C.Z3_probe_or(p.ctx.ptr, p.ptr, p2.ptr))
}
// ProbeNot creates a probe that is the negation of p.
func (p *Probe) Not() *Probe {
return newProbe(p.ctx, C.Z3_probe_not(p.ctx.ptr, p.ptr))
}
// Params represents a parameter set.
type Params struct {
ctx *Context
ptr C.Z3_params
}
// newParams creates a new Params and manages its reference count.
func newParams(ctx *Context, ptr C.Z3_params) *Params {
params := &Params{ctx: ctx, ptr: ptr}
C.Z3_params_inc_ref(ctx.ptr, ptr)
runtime.SetFinalizer(params, func(p *Params) {
C.Z3_params_dec_ref(p.ctx.ptr, p.ptr)
})
return params
}
// MkParams creates a new parameter set.
func (c *Context) MkParams() *Params {
return newParams(c, C.Z3_mk_params(c.ptr))
}
// SetBool sets a Boolean parameter.
func (p *Params) SetBool(key string, value bool) {
sym := p.ctx.MkStringSymbol(key)
C.Z3_params_set_bool(p.ctx.ptr, p.ptr, sym.ptr, C.bool(value))
}
// SetUint sets an unsigned integer parameter.
func (p *Params) SetUint(key string, value uint) {
sym := p.ctx.MkStringSymbol(key)
C.Z3_params_set_uint(p.ctx.ptr, p.ptr, sym.ptr, C.uint(value))
}
// SetDouble sets a double parameter.
func (p *Params) SetDouble(key string, value float64) {
sym := p.ctx.MkStringSymbol(key)
C.Z3_params_set_double(p.ctx.ptr, p.ptr, sym.ptr, C.double(value))
}
// SetSymbol sets a symbol parameter.
func (p *Params) SetSymbol(key string, value *Symbol) {
sym := p.ctx.MkStringSymbol(key)
C.Z3_params_set_symbol(p.ctx.ptr, p.ptr, sym.ptr, value.ptr)
}
// String returns the string representation of the parameters.
func (p *Params) String() string {
return C.GoString(C.Z3_params_to_string(p.ctx.ptr, p.ptr))
}

763
src/api/go/z3.go Normal file
View file

@ -0,0 +1,763 @@
// Package z3 provides Go bindings for the Z3 theorem prover.
//
// Z3 is a high-performance SMT (Satisfiability Modulo Theories) solver
// developed at Microsoft Research. These bindings wrap the Z3 C API using
// CGO and provide idiomatic Go interfaces with automatic memory management.
//
// # Basic Usage
//
// Create a context and solver:
//
// ctx := z3.NewContext()
// solver := ctx.NewSolver()
//
// Create variables and constraints:
//
// x := ctx.MkIntConst("x")
// y := ctx.MkIntConst("y")
// solver.Assert(ctx.MkEq(ctx.MkAdd(x, y), ctx.MkInt(10, ctx.MkIntSort())))
// solver.Assert(ctx.MkGt(x, y))
//
// Check satisfiability and get model:
//
// if solver.Check() == z3.Satisfiable {
// model := solver.Model()
// xVal, _ := model.Eval(x, true)
// fmt.Println("x =", xVal.String())
// }
//
// # Memory Management
//
// All Z3 objects are automatically managed using Go finalizers. Reference
// counting is handled transparently - you don't need to manually free objects.
//
// # Supported Features
//
// - Boolean logic, integer and real arithmetic
// - Bit-vectors and floating-point arithmetic
// - Arrays, sequences, and strings
// - Regular expressions
// - Algebraic datatypes
// - Quantifiers and lambda expressions
// - Tactics and goal-based solving
// - Optimization (MaxSMT)
// - Fixedpoint solver (Datalog/CHC)
//
// For more examples, see the examples/go directory in the Z3 repository.
package z3
/*
#cgo CFLAGS: -I${SRCDIR}/..
#cgo LDFLAGS: -lz3
#include "z3.h"
#include <stdlib.h>
*/
import "C"
import (
"runtime"
"unsafe"
)
// Config represents a Z3 configuration object.
type Config struct {
ptr C.Z3_config
}
// NewConfig creates a new Z3 configuration.
func NewConfig() *Config {
cfg := &Config{ptr: C.Z3_mk_config()}
runtime.SetFinalizer(cfg, func(c *Config) {
C.Z3_del_config(c.ptr)
})
return cfg
}
// SetParamValue sets a configuration parameter.
func (c *Config) SetParamValue(paramID, paramValue string) {
cParamID := C.CString(paramID)
cParamValue := C.CString(paramValue)
defer C.free(unsafe.Pointer(cParamID))
defer C.free(unsafe.Pointer(cParamValue))
C.Z3_set_param_value(c.ptr, cParamID, cParamValue)
}
// Context represents a Z3 logical context.
type Context struct {
ptr C.Z3_context
}
// NewContext creates a new Z3 context with default configuration.
func NewContext() *Context {
ctx := &Context{ptr: C.Z3_mk_context_rc(C.Z3_mk_config())}
runtime.SetFinalizer(ctx, func(c *Context) {
C.Z3_del_context(c.ptr)
})
return ctx
}
// NewContextWithConfig creates a new Z3 context with the given configuration.
func NewContextWithConfig(cfg *Config) *Context {
ctx := &Context{ptr: C.Z3_mk_context_rc(cfg.ptr)}
runtime.SetFinalizer(ctx, func(c *Context) {
C.Z3_del_context(c.ptr)
})
return ctx
}
// SetParam sets a global or context parameter.
func (c *Context) SetParam(key, value string) {
cKey := C.CString(key)
cValue := C.CString(value)
defer C.free(unsafe.Pointer(cKey))
defer C.free(unsafe.Pointer(cValue))
C.Z3_update_param_value(c.ptr, cKey, cValue)
}
// Symbol represents a Z3 symbol.
type Symbol struct {
ctx *Context
ptr C.Z3_symbol
}
// MkIntSymbol creates an integer symbol.
func (c *Context) MkIntSymbol(i int) *Symbol {
return &Symbol{
ctx: c,
ptr: C.Z3_mk_int_symbol(c.ptr, C.int(i)),
}
}
// MkStringSymbol creates a string symbol.
func (c *Context) MkStringSymbol(s string) *Symbol {
cStr := C.CString(s)
defer C.free(unsafe.Pointer(cStr))
return &Symbol{
ctx: c,
ptr: C.Z3_mk_string_symbol(c.ptr, cStr),
}
}
// String returns the string representation of the symbol.
func (s *Symbol) String() string {
kind := C.Z3_get_symbol_kind(s.ctx.ptr, s.ptr)
if kind == C.Z3_INT_SYMBOL {
return string(rune(C.Z3_get_symbol_int(s.ctx.ptr, s.ptr)))
}
return C.GoString(C.Z3_get_symbol_string(s.ctx.ptr, s.ptr))
}
// AST represents a Z3 abstract syntax tree node.
type AST struct {
ctx *Context
ptr C.Z3_ast
}
// incRef increments the reference count of the AST.
func (a *AST) incRef() {
C.Z3_inc_ref(a.ctx.ptr, a.ptr)
}
// decRef decrements the reference count of the AST.
func (a *AST) decRef() {
C.Z3_dec_ref(a.ctx.ptr, a.ptr)
}
// String returns the string representation of the AST.
func (a *AST) String() string {
return C.GoString(C.Z3_ast_to_string(a.ctx.ptr, a.ptr))
}
// Hash returns the hash code of the AST.
func (a *AST) Hash() uint32 {
return uint32(C.Z3_get_ast_hash(a.ctx.ptr, a.ptr))
}
// Equal checks if two ASTs are equal.
func (a *AST) Equal(other *AST) bool {
if a.ctx != other.ctx {
return false
}
return bool(C.Z3_is_eq_ast(a.ctx.ptr, a.ptr, other.ptr))
}
// Sort represents a Z3 sort (type).
type Sort struct {
ctx *Context
ptr C.Z3_sort
}
// newSort creates a new Sort and manages its reference count.
func newSort(ctx *Context, ptr C.Z3_sort) *Sort {
sort := &Sort{ctx: ctx, ptr: ptr}
C.Z3_inc_ref(ctx.ptr, C.Z3_sort_to_ast(ctx.ptr, ptr))
runtime.SetFinalizer(sort, func(s *Sort) {
C.Z3_dec_ref(s.ctx.ptr, C.Z3_sort_to_ast(s.ctx.ptr, s.ptr))
})
return sort
}
// String returns the string representation of the sort.
func (s *Sort) String() string {
return C.GoString(C.Z3_sort_to_string(s.ctx.ptr, s.ptr))
}
// Equal checks if two sorts are equal.
func (s *Sort) Equal(other *Sort) bool {
if s.ctx != other.ctx {
return false
}
return bool(C.Z3_is_eq_sort(s.ctx.ptr, s.ptr, other.ptr))
}
// MkBoolSort creates the Boolean sort.
func (c *Context) MkBoolSort() *Sort {
return newSort(c, C.Z3_mk_bool_sort(c.ptr))
}
// MkBvSort creates a bit-vector sort of the given size.
func (c *Context) MkBvSort(sz uint) *Sort {
return newSort(c, C.Z3_mk_bv_sort(c.ptr, C.uint(sz)))
}
// Expr represents a Z3 expression.
type Expr struct {
ctx *Context
ptr C.Z3_ast
}
// newExpr creates a new Expr and manages its reference count.
func newExpr(ctx *Context, ptr C.Z3_ast) *Expr {
expr := &Expr{ctx: ctx, ptr: ptr}
C.Z3_inc_ref(ctx.ptr, ptr)
runtime.SetFinalizer(expr, func(e *Expr) {
C.Z3_dec_ref(e.ctx.ptr, e.ptr)
})
return expr
}
// String returns the string representation of the expression.
func (e *Expr) String() string {
return C.GoString(C.Z3_ast_to_string(e.ctx.ptr, e.ptr))
}
// Equal checks if two expressions are equal.
func (e *Expr) Equal(other *Expr) bool {
if e.ctx != other.ctx {
return false
}
return bool(C.Z3_is_eq_ast(e.ctx.ptr, e.ptr, other.ptr))
}
// GetSort returns the sort of the expression.
func (e *Expr) GetSort() *Sort {
return newSort(e.ctx, C.Z3_get_sort(e.ctx.ptr, e.ptr))
}
// MkTrue creates the Boolean constant true.
func (c *Context) MkTrue() *Expr {
return newExpr(c, C.Z3_mk_true(c.ptr))
}
// MkFalse creates the Boolean constant false.
func (c *Context) MkFalse() *Expr {
return newExpr(c, C.Z3_mk_false(c.ptr))
}
// MkBool creates a Boolean constant.
func (c *Context) MkBool(value bool) *Expr {
if value {
return c.MkTrue()
}
return c.MkFalse()
}
// MkNumeral creates a numeral from a string.
func (c *Context) MkNumeral(numeral string, sort *Sort) *Expr {
cStr := C.CString(numeral)
defer C.free(unsafe.Pointer(cStr))
return newExpr(c, C.Z3_mk_numeral(c.ptr, cStr, sort.ptr))
}
// MkConst creates a constant (variable) with the given name and sort.
func (c *Context) MkConst(name *Symbol, sort *Sort) *Expr {
return newExpr(c, C.Z3_mk_const(c.ptr, name.ptr, sort.ptr))
}
// MkBoolConst creates a Boolean constant (variable) with the given name.
func (c *Context) MkBoolConst(name string) *Expr {
sym := c.MkStringSymbol(name)
return c.MkConst(sym, c.MkBoolSort())
}
// Boolean operations
// MkAnd creates a conjunction.
func (c *Context) MkAnd(exprs ...*Expr) *Expr {
if len(exprs) == 0 {
return c.MkTrue()
}
if len(exprs) == 1 {
return exprs[0]
}
cExprs := make([]C.Z3_ast, len(exprs))
for i, e := range exprs {
cExprs[i] = e.ptr
}
return newExpr(c, C.Z3_mk_and(c.ptr, C.uint(len(exprs)), &cExprs[0]))
}
// MkOr creates a disjunction.
func (c *Context) MkOr(exprs ...*Expr) *Expr {
if len(exprs) == 0 {
return c.MkFalse()
}
if len(exprs) == 1 {
return exprs[0]
}
cExprs := make([]C.Z3_ast, len(exprs))
for i, e := range exprs {
cExprs[i] = e.ptr
}
return newExpr(c, C.Z3_mk_or(c.ptr, C.uint(len(exprs)), &cExprs[0]))
}
// MkNot creates a negation.
func (c *Context) MkNot(expr *Expr) *Expr {
return newExpr(c, C.Z3_mk_not(c.ptr, expr.ptr))
}
// MkImplies creates an implication.
func (c *Context) MkImplies(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_implies(c.ptr, lhs.ptr, rhs.ptr))
}
// MkIff creates a bi-implication (if and only if).
func (c *Context) MkIff(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_iff(c.ptr, lhs.ptr, rhs.ptr))
}
// MkXor creates exclusive or.
func (c *Context) MkXor(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_xor(c.ptr, lhs.ptr, rhs.ptr))
}
if len(exprs) == 1 {
return exprs[0]
}
cExprs := make([]C.Z3_ast, len(exprs))
for i, e := range exprs {
cExprs[i] = e.ptr
}
return newExpr(c, C.Z3_mk_add(c.ptr, C.uint(len(exprs)), &cExprs[0]))
}
if len(exprs) == 1 {
return newExpr(c, C.Z3_mk_unary_minus(c.ptr, exprs[0].ptr))
}
cExprs := make([]C.Z3_ast, len(exprs))
for i, e := range exprs {
cExprs[i] = e.ptr
}
return newExpr(c, C.Z3_mk_sub(c.ptr, C.uint(len(exprs)), &cExprs[0]))
}
if len(exprs) == 1 {
return exprs[0]
}
cExprs := make([]C.Z3_ast, len(exprs))
for i, e := range exprs {
cExprs[i] = e.ptr
}
return newExpr(c, C.Z3_mk_mul(c.ptr, C.uint(len(exprs)), &cExprs[0]))
}
// Comparison operations
// MkEq creates an equality.
func (c *Context) MkEq(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_eq(c.ptr, lhs.ptr, rhs.ptr))
}
// MkDistinct creates a distinct constraint.
func (c *Context) MkDistinct(exprs ...*Expr) *Expr {
if len(exprs) <= 1 {
return c.MkTrue()
}
cExprs := make([]C.Z3_ast, len(exprs))
for i, e := range exprs {
cExprs[i] = e.ptr
}
return newExpr(c, C.Z3_mk_distinct(c.ptr, C.uint(len(exprs)), &cExprs[0]))
}
// FuncDecl represents a function declaration.
type FuncDecl struct {
ctx *Context
ptr C.Z3_func_decl
}
// newFuncDecl creates a new FuncDecl and manages its reference count.
func newFuncDecl(ctx *Context, ptr C.Z3_func_decl) *FuncDecl {
fd := &FuncDecl{ctx: ctx, ptr: ptr}
C.Z3_inc_ref(ctx.ptr, C.Z3_func_decl_to_ast(ctx.ptr, ptr))
runtime.SetFinalizer(fd, func(f *FuncDecl) {
C.Z3_dec_ref(f.ctx.ptr, C.Z3_func_decl_to_ast(f.ctx.ptr, f.ptr))
})
return fd
}
// String returns the string representation of the function declaration.
func (f *FuncDecl) String() string {
return C.GoString(C.Z3_func_decl_to_string(f.ctx.ptr, f.ptr))
}
// GetName returns the name of the function declaration.
func (f *FuncDecl) GetName() *Symbol {
return &Symbol{
ctx: f.ctx,
ptr: C.Z3_get_decl_name(f.ctx.ptr, f.ptr),
}
}
// GetArity returns the arity (number of parameters) of the function.
func (f *FuncDecl) GetArity() int {
return int(C.Z3_get_arity(f.ctx.ptr, f.ptr))
}
// GetDomain returns the sort of the i-th parameter.
func (f *FuncDecl) GetDomain(i int) *Sort {
return newSort(f.ctx, C.Z3_get_domain(f.ctx.ptr, f.ptr, C.uint(i)))
}
// GetRange returns the sort of the return value.
func (f *FuncDecl) GetRange() *Sort {
return newSort(f.ctx, C.Z3_get_range(f.ctx.ptr, f.ptr))
}
// MkFuncDecl creates a function declaration.
func (c *Context) MkFuncDecl(name *Symbol, domain []*Sort, range_ *Sort) *FuncDecl {
cDomain := make([]C.Z3_sort, len(domain))
for i, s := range domain {
cDomain[i] = s.ptr
}
var domainPtr *C.Z3_sort
if len(domain) > 0 {
domainPtr = &cDomain[0]
}
return newFuncDecl(c, C.Z3_mk_func_decl(c.ptr, name.ptr, C.uint(len(domain)), domainPtr, range_.ptr))
}
// MkApp creates a function application.
func (c *Context) MkApp(decl *FuncDecl, args ...*Expr) *Expr {
cArgs := make([]C.Z3_ast, len(args))
for i, a := range args {
cArgs[i] = a.ptr
}
var argsPtr *C.Z3_ast
if len(args) > 0 {
argsPtr = &cArgs[0]
}
return newExpr(c, C.Z3_mk_app(c.ptr, decl.ptr, C.uint(len(args)), argsPtr))
}
// Quantifier operations
// MkForall creates a universal quantifier.
func (c *Context) MkForall(bound []*Expr, body *Expr) *Expr {
cBound := make([]C.Z3_ast, len(bound))
for i, b := range bound {
cBound[i] = b.ptr
}
var boundPtr *C.Z3_ast
if len(bound) > 0 {
boundPtr = &cBound[0]
}
return newExpr(c, C.Z3_mk_forall_const(c.ptr, 0, C.uint(len(bound)), boundPtr, 0, nil, body.ptr))
}
// MkExists creates an existential quantifier.
func (c *Context) MkExists(bound []*Expr, body *Expr) *Expr {
cBound := make([]C.Z3_ast, len(bound))
for i, b := range bound {
cBound[i] = b.ptr
}
var boundPtr *C.Z3_ast
if len(bound) > 0 {
boundPtr = &cBound[0]
}
return newExpr(c, C.Z3_mk_exists_const(c.ptr, 0, C.uint(len(bound)), boundPtr, 0, nil, body.ptr))
}
// Simplify simplifies an expression.
func (e *Expr) Simplify() *Expr {
return newExpr(e.ctx, C.Z3_simplify(e.ctx.ptr, e.ptr))
}
// MkTypeVariable creates a type variable sort for use in polymorphic functions and datatypes
func (c *Context) MkTypeVariable(name *Symbol) *Sort {
return newSort(c, C.Z3_mk_type_variable(c.ptr, name.ptr))
}
// Quantifier represents a quantified formula (forall or exists)
type Quantifier struct {
ctx *Context
ptr C.Z3_ast
}
// newQuantifier creates a new Quantifier with proper memory management
func newQuantifier(ctx *Context, ptr C.Z3_ast) *Quantifier {
q := &Quantifier{ctx: ctx, ptr: ptr}
C.Z3_inc_ref(ctx.ptr, ptr)
runtime.SetFinalizer(q, func(qf *Quantifier) {
C.Z3_dec_ref(qf.ctx.ptr, qf.ptr)
})
return q
}
// AsExpr converts a Quantifier to an Expr
func (q *Quantifier) AsExpr() *Expr {
return newExpr(q.ctx, q.ptr)
}
// IsUniversal returns true if this is a universal quantifier (forall)
func (q *Quantifier) IsUniversal() bool {
return C.Z3_is_quantifier_forall(q.ctx.ptr, q.ptr) != 0
}
// IsExistential returns true if this is an existential quantifier (exists)
func (q *Quantifier) IsExistential() bool {
return C.Z3_is_quantifier_exists(q.ctx.ptr, q.ptr) != 0
}
// GetWeight returns the weight of the quantifier
func (q *Quantifier) GetWeight() int {
return int(C.Z3_get_quantifier_weight(q.ctx.ptr, q.ptr))
}
// GetNumPatterns returns the number of patterns
func (q *Quantifier) GetNumPatterns() int {
return int(C.Z3_get_quantifier_num_patterns(q.ctx.ptr, q.ptr))
}
// GetPattern returns the pattern at the given index
func (q *Quantifier) GetPattern(idx int) *Pattern {
ptr := C.Z3_get_quantifier_pattern_ast(q.ctx.ptr, q.ptr, C.uint(idx))
return newPattern(q.ctx, ptr)
}
// GetNumNoPatterns returns the number of no-patterns
func (q *Quantifier) GetNumNoPatterns() int {
return int(C.Z3_get_quantifier_num_no_patterns(q.ctx.ptr, q.ptr))
}
// GetNoPattern returns the no-pattern at the given index
func (q *Quantifier) GetNoPattern(idx int) *Pattern {
ptr := C.Z3_get_quantifier_no_pattern_ast(q.ctx.ptr, q.ptr, C.uint(idx))
return newPattern(q.ctx, ptr)
}
// GetNumBound returns the number of bound variables
func (q *Quantifier) GetNumBound() int {
return int(C.Z3_get_quantifier_num_bound(q.ctx.ptr, q.ptr))
}
// GetBoundName returns the name of the bound variable at the given index
func (q *Quantifier) GetBoundName(idx int) *Symbol {
ptr := C.Z3_get_quantifier_bound_name(q.ctx.ptr, q.ptr, C.uint(idx))
return newSymbol(q.ctx, ptr)
}
// GetBoundSort returns the sort of the bound variable at the given index
func (q *Quantifier) GetBoundSort(idx int) *Sort {
ptr := C.Z3_get_quantifier_bound_sort(q.ctx.ptr, q.ptr, C.uint(idx))
return newSort(q.ctx, ptr)
}
// GetBody returns the body of the quantifier
func (q *Quantifier) GetBody() *Expr {
ptr := C.Z3_get_quantifier_body(q.ctx.ptr, q.ptr)
return newExpr(q.ctx, ptr)
}
// String returns the string representation of the quantifier
func (q *Quantifier) String() string {
return q.AsExpr().String()
}
// MkQuantifier creates a quantifier with patterns
func (c *Context) MkQuantifier(isForall bool, weight int, sorts []*Sort, names []*Symbol, body *Expr, patterns []*Pattern) *Quantifier {
var forallInt C.int
if isForall {
forallInt = 1
} else {
forallInt = 0
}
numBound := len(sorts)
if numBound != len(names) {
panic("Number of sorts must match number of names")
}
var cSorts []C.Z3_sort
var cNames []C.Z3_symbol
if numBound > 0 {
cSorts = make([]C.Z3_sort, numBound)
cNames = make([]C.Z3_symbol, numBound)
for i := 0; i < numBound; i++ {
cSorts[i] = sorts[i].ptr
cNames[i] = names[i].ptr
}
}
var cPatterns []C.Z3_pattern
var patternsPtr *C.Z3_pattern
numPatterns := len(patterns)
if numPatterns > 0 {
cPatterns = make([]C.Z3_pattern, numPatterns)
for i := 0; i < numPatterns; i++ {
cPatterns[i] = patterns[i].ptr
}
patternsPtr = &cPatterns[0]
}
var sortsPtr *C.Z3_sort
var namesPtr *C.Z3_symbol
if numBound > 0 {
sortsPtr = &cSorts[0]
namesPtr = &cNames[0]
}
ptr := C.Z3_mk_quantifier(c.ptr, forallInt, C.uint(weight), C.uint(numPatterns), patternsPtr,
C.uint(numBound), sortsPtr, namesPtr, body.ptr)
return newQuantifier(c, ptr)
}
// MkQuantifierConst creates a quantifier using constant bound variables
func (c *Context) MkQuantifierConst(isForall bool, weight int, bound []*Expr, body *Expr, patterns []*Pattern) *Quantifier {
var forallInt C.int
if isForall {
forallInt = 1
} else {
forallInt = 0
}
numBound := len(bound)
var cBound []C.Z3_app
var boundPtr *C.Z3_app
if numBound > 0 {
cBound = make([]C.Z3_app, numBound)
for i := 0; i < numBound; i++ {
cBound[i] = C.Z3_app(bound[i].ptr)
}
boundPtr = &cBound[0]
}
var cPatterns []C.Z3_pattern
var patternsPtr *C.Z3_pattern
numPatterns := len(patterns)
if numPatterns > 0 {
cPatterns = make([]C.Z3_pattern, numPatterns)
for i := 0; i < numPatterns; i++ {
cPatterns[i] = patterns[i].ptr
}
patternsPtr = &cPatterns[0]
}
ptr := C.Z3_mk_quantifier_const(c.ptr, forallInt, C.uint(weight), C.uint(numBound), boundPtr,
C.uint(numPatterns), patternsPtr, body.ptr)
return newQuantifier(c, ptr)
}
// Lambda represents a lambda expression
type Lambda struct {
ctx *Context
ptr C.Z3_ast
}
// newLambda creates a new Lambda with proper memory management
func newLambda(ctx *Context, ptr C.Z3_ast) *Lambda {
l := &Lambda{ctx: ctx, ptr: ptr}
C.Z3_inc_ref(ctx.ptr, ptr)
runtime.SetFinalizer(l, func(lam *Lambda) {
C.Z3_dec_ref(lam.ctx.ptr, lam.ptr)
})
return l
}
// AsExpr converts a Lambda to an Expr
func (l *Lambda) AsExpr() *Expr {
return newExpr(l.ctx, l.ptr)
}
// GetNumBound returns the number of bound variables
func (l *Lambda) GetNumBound() int {
return int(C.Z3_get_quantifier_num_bound(l.ctx.ptr, l.ptr))
}
// GetBoundName returns the name of the bound variable at the given index
func (l *Lambda) GetBoundName(idx int) *Symbol {
ptr := C.Z3_get_quantifier_bound_name(l.ctx.ptr, l.ptr, C.uint(idx))
return newSymbol(l.ctx, ptr)
}
// GetBoundSort returns the sort of the bound variable at the given index
func (l *Lambda) GetBoundSort(idx int) *Sort {
ptr := C.Z3_get_quantifier_bound_sort(l.ctx.ptr, l.ptr, C.uint(idx))
return newSort(l.ctx, ptr)
}
// GetBody returns the body of the lambda expression
func (l *Lambda) GetBody() *Expr {
ptr := C.Z3_get_quantifier_body(l.ctx.ptr, l.ptr)
return newExpr(l.ctx, ptr)
}
// String returns the string representation of the lambda
func (l *Lambda) String() string {
return l.AsExpr().String()
}
// MkLambda creates a lambda expression with sorts and names
func (c *Context) MkLambda(sorts []*Sort, names []*Symbol, body *Expr) *Lambda {
numBound := len(sorts)
if numBound != len(names) {
panic("Number of sorts must match number of names")
}
var cSorts []C.Z3_sort
var cNames []C.Z3_symbol
var sortsPtr *C.Z3_sort
var namesPtr *C.Z3_symbol
if numBound > 0 {
cSorts = make([]C.Z3_sort, numBound)
cNames = make([]C.Z3_symbol, numBound)
for i := 0; i < numBound; i++ {
cSorts[i] = sorts[i].ptr
cNames[i] = names[i].ptr
}
sortsPtr = &cSorts[0]
namesPtr = &cNames[0]
}
ptr := C.Z3_mk_lambda(c.ptr, C.uint(numBound), sortsPtr, namesPtr, body.ptr)
return newLambda(c, ptr)
}
// MkLambdaConst creates a lambda expression using constant bound variables
func (c *Context) MkLambdaConst(bound []*Expr, body *Expr) *Lambda {
numBound := len(bound)
var cBound []C.Z3_app
var boundPtr *C.Z3_app
if numBound > 0 {
cBound = make([]C.Z3_app, numBound)
for i := 0; i < numBound; i++ {
cBound[i] = C.Z3_app(bound[i].ptr)
}
boundPtr = &cBound[0]
}
ptr := C.Z3_mk_lambda_const(c.ptr, C.uint(numBound), boundPtr, body.ptr)
return newLambda(c, ptr)
}