3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-22 16:45:31 +00:00

Merge branch 'master' into polysat

This commit is contained in:
Jakob Rath 2022-07-01 16:11:17 +02:00
commit e5e79c1d4b
398 changed files with 24548 additions and 4983 deletions

View file

@ -21,6 +21,7 @@
#include "ast/for_each_expr.h"
#include "ast/rewriter/bv_rewriter.h"
#include "ast/rewriter/bool_rewriter.h"
#include <iostream>
struct lackr_model_constructor::imp {
public:
@ -186,7 +187,7 @@ private:
return m_app2val.find(a, val);
}
bool evaluate(app * const a, expr_ref& result) {
bool evaluate(app * a, expr_ref& result) {
SASSERT(!is_val(a));
const unsigned num = a->get_num_args();
if (num == 0) { // handle constants
@ -232,20 +233,20 @@ private:
// Check and record the value for a given term, given that all arguments are already checked.
//
bool mk_value(app * a) {
if (is_val(a)) return true; // skip numerals
if (is_val(a))
return true; // skip numerals
TRACE("model_constructor", tout << "mk_value(\n" << mk_ismt2_pp(a, m, 2) << ")\n";);
SASSERT(!m_app2val.contains(a));
expr_ref result(m);
if (!evaluate(a, result)) return false;
SASSERT(is_val(result));
if (!evaluate(a, result))
return false;
TRACE("model_constructor",
tout << "map term(\n" << mk_ismt2_pp(a, m, 2) << "\n->"
<< mk_ismt2_pp(result.get(), m, 2)<< ")\n"; );
CTRACE("model_constructor",
!is_val(result.get()),
tout << "eval fail\n" << mk_ismt2_pp(a, m, 2) << mk_ismt2_pp(result, m, 2) << "\n";
tout << "eval didn't create a constant \n" << mk_ismt2_pp(a, m, 2) << " " << mk_ismt2_pp(result, m, 2) << "\n";
);
SASSERT(is_val(result.get()));
m_app2val.insert(a, result.get()); // memoize
m_pinned.push_back(a);
m_pinned.push_back(result);

View file

@ -15,7 +15,6 @@ Author:
Revision History:
--*/
#include<iostream>
#include "api/api_log_macros.h"
#include "api/api_context.h"
#include "api/api_util.h"
@ -874,6 +873,91 @@ extern "C" {
Z3_CATCH_RETURN(nullptr);
}
Z3_ast Z3_API Z3_substitute_funs(Z3_context c,
Z3_ast _a,
unsigned num_funs,
Z3_func_decl const _from[],
Z3_ast const _to[]) {
Z3_TRY;
LOG_Z3_substitute_funs(c, _a, num_funs, _from, _to);
RESET_ERROR_CODE();
ast_manager & m = mk_c(c)->m();
expr * a = to_expr(_a);
func_decl * const * from = to_func_decls(_from);
expr * const * to = to_exprs(num_funs, _to);
expr * r = nullptr, *v, *w;
expr_ref_vector trail(m), args(m);
ptr_vector<expr> todo;
obj_map<func_decl, expr*> rep;
obj_map<expr, expr*> cache;
for (unsigned i = 0; i < num_funs; i++) {
if (from[i]->get_range() != to[i]->get_sort()) {
SET_ERROR_CODE(Z3_SORT_ERROR, nullptr);
RETURN_Z3(of_expr(nullptr));
}
rep.insert(from[i], to[i]);
}
var_subst subst(m, false);
todo.push_back(a);
while (!todo.empty()) {
r = todo.back();
if (cache.contains(r))
todo.pop_back();
else if (is_app(r)) {
args.reset();
unsigned sz = todo.size();
bool change = false;
for (expr* arg : *to_app(r)) {
if (cache.find(arg, v)) {
args.push_back(v);
change |= v != arg;
}
else {
todo.push_back(arg);
}
}
if (todo.size() == sz) {
if (rep.find(to_app(r)->get_decl(), w)) {
expr_ref new_v = subst(w, args);
v = new_v;
trail.push_back(v);
}
else if (change) {
v = m.mk_app(to_app(r)->get_decl(), args);
trail.push_back(v);
}
else
v = r;
cache.insert(r, v);
todo.pop_back();
}
}
else if (is_var(r)) {
cache.insert(r, r);
todo.pop_back();
}
else if (is_quantifier(r)) {
if (cache.find(to_quantifier(r)->get_expr(), v)) {
v = m.update_quantifier(to_quantifier(r), v);
trail.push_back(v);
cache.insert(r, v);
todo.pop_back();
}
else
todo.push_back(to_quantifier(r)->get_expr());
}
else
UNREACHABLE();
}
r = cache[a];
mk_c(c)->save_ast_trail(r);
RETURN_Z3(of_expr(r));
Z3_CATCH_RETURN(nullptr);
}
Z3_ast Z3_API Z3_substitute_vars(Z3_context c,
Z3_ast _a,
unsigned num_exprs,

View file

@ -15,7 +15,6 @@ Author:
Revision History:
--*/
#include<iostream>
#include "api/z3.h"
#include "api/api_log_macros.h"
#include "api/api_context.h"
@ -52,7 +51,6 @@ extern "C" {
void Z3_API Z3_ast_map_dec_ref(Z3_context c, Z3_ast_map m) {
Z3_TRY;
LOG_Z3_ast_map_dec_ref(c, m);
RESET_ERROR_CODE();
if (m)
to_ast_map(m)->dec_ref();
Z3_CATCH;

View file

@ -15,7 +15,6 @@ Author:
Revision History:
--*/
#include<iostream>
#include "api/z3.h"
#include "api/api_log_macros.h"
#include "api/api_context.h"
@ -47,7 +46,6 @@ extern "C" {
void Z3_API Z3_ast_vector_dec_ref(Z3_context c, Z3_ast_vector v) {
Z3_TRY;
LOG_Z3_ast_vector_dec_ref(c, v);
RESET_ERROR_CODE();
if (v)
to_ast_vector(v)->dec_ref();
Z3_CATCH;

View file

@ -102,6 +102,7 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \
MK_BV_PUNARY(Z3_mk_sign_ext, OP_SIGN_EXT);
MK_BV_PUNARY(Z3_mk_zero_ext, OP_ZERO_EXT);
MK_BV_PUNARY(Z3_mk_repeat, OP_REPEAT);
MK_BV_PUNARY(Z3_mk_bit2bool, OP_BIT2BOOL);
MK_BV_PUNARY(Z3_mk_rotate_left, OP_ROTATE_LEFT);
MK_BV_PUNARY(Z3_mk_rotate_right, OP_ROTATE_RIGHT);
MK_BV_PUNARY(Z3_mk_int2bv, OP_INT2BV);

View file

@ -64,6 +64,17 @@ extern "C" {
}
}
Z3_param_descrs Z3_API Z3_get_global_param_descrs(Z3_context c) {
Z3_TRY;
LOG_Z3_get_global_param_descrs(c);
Z3_param_descrs_ref * d = alloc(Z3_param_descrs_ref, *mk_c(c));
mk_c(c)->save_object(d);
d->m_descrs = gparams::get_global_param_descrs();
auto r = of_param_descrs(d);
RETURN_Z3(r);
Z3_CATCH_RETURN(nullptr);
}
Z3_config Z3_API Z3_mk_config(void) {
try {
memory::initialize(UINT_MAX);

View file

@ -113,6 +113,7 @@ namespace api {
}
if (m_params.owns_manager())
m_manager.detach();
}
context::set_interruptable::set_interruptable(context & ctx, event_handler & i):
@ -371,8 +372,9 @@ extern "C" {
void Z3_API Z3_dec_ref(Z3_context c, Z3_ast a) {
Z3_TRY;
LOG_Z3_dec_ref(c, a);
RESET_ERROR_CODE();
if (a && to_ast(a)->get_ref_count() == 0) {
// the error is unchecked (but should not happen) in GC'ed wrappers
RESET_ERROR_CODE();
SET_ERROR_CODE(Z3_DEC_REF_ERROR, nullptr);
return;
}

View file

@ -365,6 +365,20 @@ extern "C" {
Z3_CATCH;
}
Z3_sort Z3_API Z3_mk_datatype_sort(Z3_context c, Z3_symbol name) {
Z3_TRY;
LOG_Z3_mk_datatype_sort(c, name);
RESET_ERROR_CODE();
ast_manager& m = mk_c(c)->m();
datatype_util adt_util(m);
parameter p(to_symbol(name));
sort * s = m.mk_sort(adt_util.get_family_id(), DATATYPE_SORT, 1, &p);
mk_c(c)->save_ast_trail(s);
RETURN_Z3(of_sort(s));
Z3_CATCH_RETURN(nullptr);
}
void Z3_API Z3_mk_datatypes(Z3_context c,
unsigned num_sorts,
Z3_symbol const sort_names[],

View file

@ -16,7 +16,6 @@ Author:
Notes:
--*/
#include<iostream>
#include "api/z3.h"
#include "api/api_log_macros.h"
#include "api/api_context.h"

View file

@ -15,7 +15,6 @@ Author:
Revision History:
--*/
#include<iostream>
#include "api/z3.h"
#include "api/api_log_macros.h"
#include "api/api_context.h"
@ -52,7 +51,6 @@ extern "C" {
void Z3_API Z3_goal_dec_ref(Z3_context c, Z3_goal g) {
Z3_TRY;
LOG_Z3_goal_dec_ref(c, g);
RESET_ERROR_CODE();
if (g)
to_goal(g)->dec_ref();
Z3_CATCH;

View file

@ -53,7 +53,6 @@ extern "C" {
void Z3_API Z3_model_dec_ref(Z3_context c, Z3_model m) {
Z3_TRY;
LOG_Z3_model_dec_ref(c, m);
RESET_ERROR_CODE();
if (m) {
to_model(m)->dec_ref();
}

View file

@ -16,7 +16,6 @@ Revision History:
--*/
#include<cmath>
#include<iostream>
#include "api/z3.h"
#include "api/api_log_macros.h"
#include "api/api_context.h"

View file

@ -15,7 +15,6 @@ Author:
Revision History:
--*/
#include<iostream>
#include "util/cancel_eh.h"
#include "util/scoped_timer.h"
#include "util/scoped_ctrl_c.h"
@ -67,7 +66,6 @@ extern "C" {
void Z3_API Z3_optimize_dec_ref(Z3_context c, Z3_optimize o) {
Z3_TRY;
LOG_Z3_optimize_dec_ref(c, o);
RESET_ERROR_CODE();
if (o)
to_optimize(o)->dec_ref();
Z3_CATCH;

View file

@ -17,7 +17,6 @@ Author:
Revision History:
--*/
#include<iostream>
#include "api/z3.h"
#include "api/api_log_macros.h"
#include "api/api_context.h"
@ -54,7 +53,6 @@ extern "C" {
void Z3_API Z3_params_dec_ref(Z3_context c, Z3_params p) {
Z3_TRY;
LOG_Z3_params_dec_ref(c, p);
RESET_ERROR_CODE();
if (p)
to_params(p)->dec_ref();
Z3_CATCH;
@ -141,8 +139,8 @@ extern "C" {
void Z3_API Z3_param_descrs_dec_ref(Z3_context c, Z3_param_descrs p) {
Z3_TRY;
LOG_Z3_param_descrs_dec_ref(c, p);
RESET_ERROR_CODE();
to_param_descrs(p)->dec_ref();
if (p)
to_param_descrs(p)->dec_ref();
Z3_CATCH;
}

View file

@ -15,7 +15,6 @@ Author:
Revision History:
--*/
#include<iostream>
#include "api/z3.h"
#include "api/api_log_macros.h"
#include "api/api_context.h"

View file

@ -17,7 +17,6 @@ Notes:
--*/
#include <iostream>
#include "ast/expr_map.h"
#include "api/z3.h"
#include "api/api_log_macros.h"

View file

@ -19,7 +19,6 @@ Author:
Notes:
--*/
#include<iostream>
#include "api/z3.h"
#include "api/api_log_macros.h"
#include "api/api_context.h"

View file

@ -16,7 +16,6 @@ Author:
Revision History:
--*/
#include<iostream>
#include<thread>
#include "util/scoped_ctrl_c.h"
#include "util/cancel_eh.h"
@ -207,7 +206,7 @@ extern "C" {
if (!smt_logics::supported_logic(to_symbol(logic))) {
std::ostringstream strm;
strm << "logic '" << to_symbol(logic) << "' is not recognized";
throw default_exception(strm.str());
SET_ERROR_CODE(Z3_INVALID_ARG, strm.str());
RETURN_Z3(nullptr);
}
else {
@ -412,9 +411,8 @@ extern "C" {
void Z3_API Z3_solver_dec_ref(Z3_context c, Z3_solver s) {
Z3_TRY;
LOG_Z3_solver_dec_ref(c, s);
RESET_ERROR_CODE();
if (s)
to_solver(s)->dec_ref();
if (s)
to_solver(s)->dec_ref();
Z3_CATCH;
}
@ -983,6 +981,14 @@ extern "C" {
Z3_CATCH;
}
void Z3_API Z3_solver_next_split(Z3_context c, Z3_solver_callback cb, Z3_ast t, unsigned idx, Z3_lbool phase) {
Z3_TRY;
LOG_Z3_solver_next_split(c, cb, t, idx, phase);
RESET_ERROR_CODE();
reinterpret_cast<user_propagator::callback*>(cb)->next_split_cb(to_expr(t), idx, (lbool)phase);
Z3_CATCH;
}
Z3_func_decl Z3_API Z3_solver_propagate_declare(Z3_context c, Z3_symbol name, unsigned n, Z3_sort* domain, Z3_sort range) {
Z3_TRY;
LOG_Z3_solver_propagate_declare(c, name, n, domain, range);

View file

@ -17,7 +17,6 @@ Revision History:
--*/
#include <iostream>
#include "api/z3.h"
#include "api/api_log_macros.h"
#include "api/api_context.h"

View file

@ -15,7 +15,6 @@ Author:
Revision History:
--*/
#include<iostream>
#include "api/z3.h"
#include "api/api_log_macros.h"
#include "api/api_context.h"

View file

@ -15,7 +15,6 @@ Author:
Revision History:
--*/
#include<iostream>
#include "api/z3.h"
#include "api/api_log_macros.h"
#include "api/api_context.h"
@ -73,7 +72,6 @@ extern "C" {
void Z3_API Z3_tactic_dec_ref(Z3_context c, Z3_tactic t) {
Z3_TRY;
LOG_Z3_tactic_dec_ref(c, t);
RESET_ERROR_CODE();
if (t)
to_tactic(t)->dec_ref();
Z3_CATCH;
@ -104,7 +102,6 @@ extern "C" {
void Z3_API Z3_probe_dec_ref(Z3_context c, Z3_probe p) {
Z3_TRY;
LOG_Z3_probe_dec_ref(c, p);
RESET_ERROR_CODE();
if (p)
to_probe(p)->dec_ref();
Z3_CATCH;
@ -477,7 +474,6 @@ extern "C" {
void Z3_API Z3_apply_result_dec_ref(Z3_context c, Z3_apply_result r) {
Z3_TRY;
LOG_Z3_apply_result_dec_ref(c, r);
RESET_ERROR_CODE();
if (r)
to_apply_result(r)->dec_ref();
Z3_CATCH;

View file

@ -21,7 +21,7 @@ Notes:
#pragma once
#include<cassert>
#include<iostream>
#include<ostream>
#include<string>
#include<sstream>
#include<memory>
@ -459,6 +459,7 @@ namespace z3 {
}
~param_descrs() { Z3_param_descrs_dec_ref(ctx(), m_descrs); }
static param_descrs simplify_param_descrs(context& c) { return param_descrs(c, Z3_simplify_get_param_descrs(c)); }
static param_descrs global_param_descrs(context& c) { return param_descrs(c, Z3_get_global_param_descrs(c)); }
unsigned size() { return Z3_param_descrs_size(ctx(), m_descrs); }
symbol name(unsigned i) { return symbol(ctx(), Z3_param_descrs_get_name(ctx(), m_descrs, i)); }
@ -1359,6 +1360,7 @@ namespace z3 {
friend expr operator~(expr const & a);
expr extract(unsigned hi, unsigned lo) const { Z3_ast r = Z3_mk_extract(ctx(), hi, lo, *this); ctx().check_error(); return expr(ctx(), r); }
expr bit2bool(unsigned i) const { Z3_ast r = Z3_mk_bit2bool(ctx(), i, *this); ctx().check_error(); return expr(ctx(), r); }
unsigned lo() const { assert (is_app() && Z3_get_decl_num_parameters(ctx(), decl()) == 2); return static_cast<unsigned>(Z3_get_decl_int_parameter(ctx(), decl(), 1)); }
unsigned hi() const { assert (is_app() && Z3_get_decl_num_parameters(ctx(), decl()) == 2); return static_cast<unsigned>(Z3_get_decl_int_parameter(ctx(), decl(), 0)); }
@ -4156,6 +4158,11 @@ namespace z3 {
virtual void decide(expr& /*val*/, unsigned& /*bit*/, Z3_lbool& /*is_pos*/) {}
void next_split(expr const & e, unsigned idx, Z3_lbool phase) {
assert(cb);
Z3_solver_next_split(ctx(), cb, e, idx, phase);
}
/**
\brief tracks \c e by a unique identifier that is returned by the call.

View file

@ -113,6 +113,7 @@ set(Z3_DOTNET_ASSEMBLY_SOURCES_IN_SRC_TREE
Tactic.cs
TupleSort.cs
UninterpretedSort.cs
UserPropagator.cs
Version.cs
Z3Exception.cs
Z3Object.cs

View file

@ -25,6 +25,8 @@ using System.Runtime.InteropServices;
namespace Microsoft.Z3
{
using Z3_context = System.IntPtr;
/// <summary>
/// The main interaction with Z3 happens via the Context.
/// </summary>
@ -77,6 +79,23 @@ namespace Microsoft.Z3
InitContext();
}
}
/// <summary>
/// Internal Constructor. It is used from UserPropagator
/// </summary>
internal Context(Z3_context ctx)
: base()
{
lock (creation_lock)
{
is_external = true;
m_ctx = ctx;
InitContext();
}
}
bool is_external = false;
#endregion
#region Symbols
@ -669,6 +688,16 @@ namespace Microsoft.Z3
CheckContextMatch(range);
return new FuncDecl(this, prefix, null, range);
}
/// <summary>
/// Declare a function to be processed by the user propagator plugin.
/// </summary>
public FuncDecl MkUserPropagatorFuncDecl(string name, Sort[] domain, Sort range)
{
using var _name = MkSymbol(name);
var fn = Native.Z3_solver_propagate_declare(nCtx, _name.NativeObject, AST.ArrayLength(domain), AST.ArrayToNative(domain), range.NativeObject);
return new FuncDecl(this, fn);
}
#endregion
#region Bound Variables
@ -4978,11 +5007,14 @@ namespace Microsoft.Z3
m_n_err_handler = null;
IntPtr ctx = m_ctx;
m_ctx = IntPtr.Zero;
Native.Z3_del_context(ctx);
if (!is_external)
Native.Z3_del_context(ctx);
}
else
GC.ReRegisterForFinalize(this);
}
#endregion
}
}

View file

@ -1341,13 +1341,13 @@ namespace Microsoft.Z3
#region Tracing
/// <summary>
/// Enable tracint to file
/// Enable trace to file
/// </summary>
/// <param name="file"></param>
public void TraceToFile(string file)
/// <param name="tag">Tag to trace</param>
public static void EnableTrace(string tag)
{
Debug.Assert(!string.IsNullOrEmpty(file));
Native.Z3_enable_trace(file);
Debug.Assert(!string.IsNullOrEmpty(tag));
Native.Z3_enable_trace(tag);
}
#endregion

View file

@ -0,0 +1,362 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
UserPropagator.cs
Abstract:
User Propagator plugin
Author:
Nikolaj Bjorner (nbjorner) 2022-05-07
Notes:
--*/
using System;
using System.Diagnostics;
using System.Linq;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace Microsoft.Z3
{
using Z3_solver_callback = System.IntPtr;
using Z3_context = System.IntPtr;
using Z3_solver = System.IntPtr;
using voidp = System.IntPtr;
using Z3_ast = System.IntPtr;
/// <summary>
/// Propagator context for .Net
/// </summary>
public class UserPropagator
{
/// <summary>
/// Delegate type for fixed callback
/// </summary>
public delegate void FixedEh(Expr term, Expr value);
/// <summary>
/// Delegate type for equality or disequality callback
/// </summary>
public delegate void EqEh(Expr term, Expr value);
/// <summary>
/// Delegate type for when a new term using a registered function symbol is created internally
/// </summary>
public delegate void CreatedEh(Expr term);
/// <summary>
/// Delegate type for callback into solver's branching
/// <param name="term">A bit-vector or Boolean used for branching</param>
/// <param name="idx">If the term is a bit-vector, then an index into the bit-vector being branched on</param>
/// <param name="phase">Set phase to -1 (false) or 1 (true) to override solver's phase</param>
/// </summary>
public delegate void DecideEh(ref Expr term, ref uint idx, ref int phase);
// access managed objects through a static array.
// thread safety is ignored for now.
static List<UserPropagator> propagators = new List<UserPropagator>();
int id;
Solver solver;
Context ctx;
Z3_solver_callback callback = IntPtr.Zero;
FixedEh fixed_eh;
Action final_eh;
EqEh eq_eh;
EqEh diseq_eh;
CreatedEh created_eh;
DecideEh decide_eh;
Native.Z3_push_eh push_eh;
Native.Z3_pop_eh pop_eh;
Native.Z3_fresh_eh fresh_eh;
Native.Z3_fixed_eh fixed_wrapper;
Native.Z3_final_eh final_wrapper;
Native.Z3_eq_eh eq_wrapper;
Native.Z3_eq_eh diseq_wrapper;
Native.Z3_decide_eh decide_wrapper;
Native.Z3_created_eh created_wrapper;
void Callback(Action fn, Z3_solver_callback cb)
{
this.callback = cb;
try
{
fn();
}
catch
{
// TBD: add debug log or exception handler
}
finally
{
this.callback = IntPtr.Zero;
}
}
static void _push(voidp ctx, Z3_solver_callback cb)
{
var prop = propagators[ctx.ToInt32()];
prop.Callback(() => prop.Push(), cb);
}
static void _pop(voidp ctx, Z3_solver_callback cb, uint num_scopes)
{
var prop = propagators[ctx.ToInt32()];
prop.Callback(() => prop.Pop(num_scopes), cb);
}
static voidp _fresh(voidp _ctx, Z3_context new_context)
{
var prop = propagators[_ctx.ToInt32()];
var ctx = new Context(new_context);
var prop1 = prop.Fresh(prop.ctx);
return new IntPtr(prop1.id);
}
static void _fixed(voidp ctx, Z3_solver_callback cb, Z3_ast _term, Z3_ast _value)
{
var prop = propagators[ctx.ToInt32()];
using var term = Expr.Create(prop.ctx, _term);
using var value = Expr.Create(prop.ctx, _value);
prop.Callback(() => prop.fixed_eh(term, value), cb);
}
static void _final(voidp ctx, Z3_solver_callback cb)
{
var prop = propagators[ctx.ToInt32()];
prop.Callback(() => prop.final_eh(), cb);
}
static void _eq(voidp ctx, Z3_solver_callback cb, Z3_ast a, Z3_ast b)
{
var prop = propagators[ctx.ToInt32()];
using var s = Expr.Create(prop.ctx, a);
using var t = Expr.Create(prop.ctx, b);
prop.Callback(() => prop.eq_eh(s, t), cb);
}
static void _diseq(voidp ctx, Z3_solver_callback cb, Z3_ast a, Z3_ast b)
{
var prop = propagators[ctx.ToInt32()];
using var s = Expr.Create(prop.ctx, a);
using var t = Expr.Create(prop.ctx, b);
prop.Callback(() => prop.diseq_eh(s, t), cb);
}
static void _created(voidp ctx, Z3_solver_callback cb, Z3_ast a)
{
var prop = propagators[ctx.ToInt32()];
using var t = Expr.Create(prop.ctx, a);
prop.Callback(() => prop.created_eh(t), cb);
}
static void _decide(voidp ctx, Z3_solver_callback cb, ref Z3_ast a, ref uint idx, ref int phase)
{
var prop = propagators[ctx.ToInt32()];
var t = Expr.Create(prop.ctx, a);
var u = t;
prop.callback = cb;
prop.decide_eh(ref t, ref idx, ref phase);
prop.callback = IntPtr.Zero;
if (u != t)
a = t.NativeObject;
}
/// <summary>
/// Propagator constructor from a solver class.
/// </summary>
public UserPropagator(Solver s)
{
id = propagators.Count;
propagators.Add(this);
solver = s;
ctx = solver.Context;
push_eh = _push;
pop_eh = _pop;
fresh_eh = _fresh;
Native.Z3_solver_propagate_init(ctx.nCtx, solver.NativeObject, new IntPtr(id), push_eh, pop_eh, fresh_eh);
}
/// <summary>
/// Propagator constructor from a context. It is used from inside of Fresh.
/// </summary>
public UserPropagator(Context _ctx)
{
id = propagators.Count;
propagators.Add(this);
solver = null;
ctx = _ctx;
}
/// <summary>
/// Release provate memory.
/// </summary>
~UserPropagator()
{
propagators[id] = null;
if (solver == null)
ctx.Dispose();
}
/// <summary>
/// Virtual method for push. It must be overwritten by inherited class.
/// </summary>
public virtual void Push() { throw new Z3Exception("Push method should be overwritten"); }
/// <summary>
/// Virtual method for pop. It must be overwritten by inherited class.
/// </summary>
public virtual void Pop(uint n) { throw new Z3Exception("Pop method should be overwritten"); }
/// <summary>
/// Virtual method for fresh. It can be overwritten by inherited class.
/// </summary>
public virtual UserPropagator Fresh(Context ctx) { return new UserPropagator(ctx); }
/// <summary>
/// Declare combination of assigned expressions a conflict
/// </summary>
public void Conflict(params Expr[] terms)
{
Propagate(terms, ctx.MkFalse());
}
/// <summary>
/// Declare combination of assigned expressions a conflict
/// </summary>
public void Conflict(IEnumerable<Expr> terms)
{
Propagate(terms, ctx.MkFalse());
}
/// <summary>
/// Propagate consequence
/// </summary>
public void Propagate(IEnumerable<Expr> terms, Expr conseq)
{
var nTerms = Z3Object.ArrayToNative(terms.ToArray());
Native.Z3_solver_propagate_consequence(ctx.nCtx, this.callback, (uint)nTerms.Length, nTerms, 0u, null, null, conseq.NativeObject);
}
/// <summary>
/// Set fixed callback
/// </summary>
public FixedEh Fixed
{
set
{
this.fixed_wrapper = _fixed;
this.fixed_eh = value;
if (solver != null)
Native.Z3_solver_propagate_fixed(ctx.nCtx, solver.NativeObject, fixed_wrapper);
}
}
/// <summary>
/// Set final callback
/// </summary>
public Action Final
{
set
{
this.final_wrapper = _final;
this.final_eh = value;
if (solver != null)
Native.Z3_solver_propagate_final(ctx.nCtx, solver.NativeObject, final_wrapper);
}
}
/// <summary>
/// Set equality event callback
/// </summary>
public EqEh Eq
{
set
{
this.eq_wrapper = _eq;
this.eq_eh = value;
if (solver != null)
Native.Z3_solver_propagate_eq(ctx.nCtx, solver.NativeObject, eq_wrapper);
}
}
/// <summary>
/// Set disequality event callback
/// </summary>
public EqEh Diseq
{
set
{
this.diseq_wrapper = _diseq;
this.diseq_eh = value;
if (solver != null)
Native.Z3_solver_propagate_diseq(ctx.nCtx, solver.NativeObject, diseq_wrapper);
}
}
/// <summary>
/// Set created callback
/// </summary>
public CreatedEh Created
{
set
{
this.created_wrapper = _created;
this.created_eh = value;
if (solver != null)
Native.Z3_solver_propagate_created(ctx.nCtx, solver.NativeObject, created_wrapper);
}
}
/// <summary>
/// Set decision callback
/// </summary>
public DecideEh Decide
{
set
{
this.decide_wrapper = _decide;
this.decide_eh = value;
if (solver != null)
Native.Z3_solver_propagate_decide(ctx.nCtx, solver.NativeObject, decide_wrapper);
}
}
/// <summary>
/// Set the next decision
/// </summary>
public void NextSplit(Expr e, uint idx, int phase)
{
Native.Z3_solver_next_split(ctx.nCtx, this.callback, e.NativeObject, idx, phase);
}
/// <summary>
/// Track assignments to a term
/// </summary>
public void Register(Expr term)
{
if (this.callback != IntPtr.Zero)
{
Native.Z3_solver_propagate_register_cb(ctx.nCtx, callback, term.NativeObject);
}
else
{
Native.Z3_solver_propagate_register(ctx.nCtx, solver.NativeObject, term.NativeObject);
}
}
}
}

View file

@ -113,7 +113,10 @@ namespace Microsoft.Z3
return s.NativeObject;
}
internal Context Context
/// <summary>
/// Access Context object
/// </summary>
public Context Context
{
get
{

View file

@ -19,6 +19,8 @@ add_custom_command(OUTPUT "${Z3_JAVA_NATIVE_JAVA}" "${Z3_JAVA_NATIVE_CPP}"
COMMAND "${PYTHON_EXECUTABLE}"
"${PROJECT_SOURCE_DIR}/scripts/update_api.py"
${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN}
"--java-input-dir"
"${CMAKE_CURRENT_SOURCE_DIR}"
"--java-output-dir"
"${CMAKE_CURRENT_BINARY_DIR}"
"--java-package-name"

View file

@ -0,0 +1,79 @@
// Automatically generated file
#include<jni.h>
#include<stdlib.h>
#include"z3.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __GNUC__
#if __GNUC__ >= 4
#define DLL_VIS __attribute__ ((visibility ("default")))
#else
#define DLL_VIS
#endif
#else
#define DLL_VIS
#endif
#if defined(__LP64__) || defined(_WIN64)
#define GETLONGAELEMS(T,OLD,NEW) \
T * NEW = (OLD == 0) ? 0 : (T*) jenv->GetLongArrayElements(OLD, NULL);
#define RELEASELONGAELEMS(OLD,NEW) \
if (OLD != 0) jenv->ReleaseLongArrayElements(OLD, (jlong *) NEW, JNI_ABORT);
#define GETLONGAREGION(T,OLD,Z,SZ,NEW) \
jenv->GetLongArrayRegion(OLD,Z,(jsize)SZ,(jlong*)NEW);
#define SETLONGAREGION(OLD,Z,SZ,NEW) \
jenv->SetLongArrayRegion(OLD,Z,(jsize)SZ,(jlong*)NEW)
#else
#define GETLONGAELEMS(T,OLD,NEW) \
T * NEW = 0; { \
jlong * temp = (OLD == 0) ? 0 : jenv->GetLongArrayElements(OLD, NULL); \
unsigned int size = (OLD == 0) ? 0 :jenv->GetArrayLength(OLD); \
if (OLD != 0) { \
NEW = (T*) (new int[size]); \
for (unsigned i=0; i < size; i++) \
NEW[i] = reinterpret_cast<T>(temp[i]); \
jenv->ReleaseLongArrayElements(OLD, temp, JNI_ABORT); \
} \
}
#define RELEASELONGAELEMS(OLD,NEW) \
delete [] NEW;
#define GETLONGAREGION(T,OLD,Z,SZ,NEW) \
{ \
jlong * temp = new jlong[SZ]; \
jenv->GetLongArrayRegion(OLD,Z,(jsize)SZ,(jlong*)temp); \
for (int i = 0; i < (SZ); i++) \
NEW[i] = reinterpret_cast<T>(temp[i]); \
delete [] temp; \
}
#define SETLONGAREGION(OLD,Z,SZ,NEW) \
{ \
jlong * temp = new jlong[SZ]; \
for (int i = 0; i < (SZ); i++) \
temp[i] = reinterpret_cast<jlong>(NEW[i]); \
jenv->SetLongArrayRegion(OLD,Z,(jsize)SZ,temp); \
delete [] temp; \
}
#endif
void Z3JavaErrorHandler(Z3_context c, Z3_error_code e)
{
// Internal do-nothing error handler. This is required to avoid that Z3 calls exit()
// upon errors, but the actual error handling is done by throwing exceptions in the
// wrappers below.
}
DLL_VIS JNIEXPORT void JNICALL Java_com_microsoft_z3_Native_setInternalErrorHandler(JNIEnv * jenv, jclass cls, jlong a0)
{
Z3_set_error_handler((Z3_context)a0, Z3JavaErrorHandler);
}

1
src/api/js/.nvmrc Normal file
View file

@ -0,0 +1 @@
v16.15.0

View file

@ -0,0 +1,6 @@
{
"singleQuote": true,
"arrowParens": "avoid",
"printWidth": 120,
"trailingComma": "all"
}

View file

@ -1,30 +1,86 @@
# Z3 TypeScript Bindings
This project provides low-level TypeScript bindings for the [Z3 theorem prover](https://github.com/Z3Prover/z3). It is available on npm as [z3-solver](https://www.npmjs.com/package/z3-solver).
Z3 itself is distributed as a wasm artifact as part of this package. You can find the documentation for the Z3 API [here](https://z3prover.github.io/api/html/z3__api_8h.html), though note the differences below.
This project provides high-level and low-level TypeScript bindings for the [Z3 theorem prover](https://github.com/Z3Prover/z3). It is available on npm as [z3-solver](https://www.npmjs.com/package/z3-solver).
Z3 itself is distributed as a wasm artifact as part of this package.
## Using
This requires threads, which means you'll need to be running in an environment which supports `SharedArrayBuffer`. In browsers, in addition to ensuring the browser has implemented `SharedArrayBuffer`, you'll need to serve your page with [special headers](https://web.dev/coop-coep/). There's a [neat trick](https://github.com/gzuidhof/coi-serviceworker) for doing that client-side on e.g. Github Pages, though you shouldn't use that trick in more complex applications.
```javascript
const { init } = require('z3-solver');
const {
Z3, // Low-level C-like API
Context, // High-level Z3Py-like API
} = await init();
```
Other than the differences below, the bindings can be used exactly as you'd use the C library. Because this is a wrapper around a C library, most of the values you'll use are just numbers representing pointers. For this reason you are strongly encouraged to make use of the TypeScript types to differentiate among the different kinds of value.
This package has different initialization for browser and node. Your bundler and node should choose good version automatically, but you can import the one you need manually - `const { init } = require('z3-solver/node');` or `const { init } = require('z3-solver/browser');`.
The module exports an `init` function, is an async function which initializes the library and returns `{ em, Z3 }` - `em` contains the underlying emscripten module, which you can use to e.g. kill stray threads, and `Z3` contains the actual bindings. The other module exports are the enums defined in the Z3 API.
### Limitations
[`test-ts-api.ts`](./test-ts-api.ts) contains a couple real cases translated very mechanically from [this file](https://github.com/Z3Prover/z3/blob/90fd3d82fce20d45ed2eececdf65545bab769503/examples/c/test_capi.c).
The package requires threads, which means you'll need to be running in an environment which supports `SharedArrayBuffer`. In browsers, in addition to ensuring the browser has implemented `SharedArrayBuffer`, you'll need to serve your page with [special headers](https://web.dev/coop-coep/). There's a [neat trick](https://github.com/gzuidhof/coi-serviceworker) for doing that client-side on e.g. Github Pages, though you shouldn't use that trick in more complex applications.
The Emscripten worker model will spawn multiple instances of `z3-built.js` for long-running operations. When building for the web, you should include that file as its own script on the page - using a bundler like webpack will prevent it from loading correctly.
## Differences from the C API
## High-level
### Integers
You can find the documentation for the high-level Z3 API [here](https://z3prover.github.io/api/html/js/index.html). There are some usage examples in `src/high-level/high-level.test.ts`
Most of the API requires a context to run, and so you need to initialize one to access most functionality.
```javascript
const { init } = require('z3-solver');
const { Context } = await init();
const { Solver, Int, And } = new Context('main');
const x = Int.const('x');
const solver = new Solver();
solver.add(And(x.ge(0), x.le(9)));
console.log(await solver.check());
// sat
```
Typescript types try to catch errors concerning mixing of Contexts during compile time. Objects returned from `new Context('name')` have a type narrowed by the provided Context name and will fail to compile if you try to mix them.
```typescript
const { Int: Int1 } = new Context('context1');
const { Int: Int2 } = new Context('context2');
const x = Int1.const('x');
const y = Int2.const('y');
// The below will fail to compile in Typescript
x.ge(y);
```
```typescript
// Use templated functions to propagate narrowed types
function add<Name extends string>(a: Arith<Name>, b: Arith<Name>): Arith<Name> {
return a.add(b).add(5);
}
```
Some long-running functions are promises and will run in a separate thread.
Currently Z3-solver is not thread safe, and so, high-level APIs ensures that only one long-running function can run at a time, and all other long-running requests will queue up and be run one after another.
## Low-level
You can find the documentation for the low-level Z3 API [here](https://z3prover.github.io/api/html/z3__api_8h.html), though note the differences below. `examples/low-level/` contains a couple real cases translated very mechanically from [this file](https://github.com/Z3Prover/z3/blob/90fd3d82fce20d45ed2eececdf65545bab769503/examples/c/test_capi.c).
The bindings can be used exactly as you'd use the C library. Because this is a wrapper around a C library, most of the values you'll use are just numbers representing pointers. For this reason you are strongly encouraged to make use of the TypeScript types to differentiate among the different kinds of value.
The module exports an `init` function, which is an async function which initializes the library and returns `{ em, Z3 }` - `em` contains the underlying emscripten module, which you can use to e.g. kill stray threads, and `Z3` contains the actual bindings. The other module exports are the enums defined in the Z3 API.
### Differences from the C API
#### Integers
JavaScript numbers are IEEE double-precisions floats. These can be used wherever the C API expects an `int`, `unsigned int`, `float`, or `double`.
`int64_t` and `uint64_t` cannot be precisely represented by JS numbers, so in the TS bindings they are represented by [BigInts](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#bigint_type).
### Out parameters
#### Out parameters
In C, to return multiple values a function will take an address to write to, conventionally referred to as an "out parameter". Sometimes the function returns a boolean to indicate success; other times it may return nothing or some other value.
@ -54,7 +110,7 @@ is represented in the TS bindings as
```ts
function model_eval(c: Z3_context, m: Z3_model, t: Z3_ast, model_completion: boolean): Z3_ast | null {
// ...
// ...
}
```
@ -62,12 +118,12 @@ Note that the boolean return type of the C function is translated into a nullabl
When the return value is of interest it is included in the returned record under the key `rv`.
### Arrays
#### Arrays
The when the C API takes an array as an argument it will also require a parameter which specifies the length of the array (since arrays do not carry their own lengths in C). In the TS bindings this is automatically inferred.
For example, the C declaration
```js
unsigned Z3_rcf_mk_roots(Z3_context c, unsigned n, Z3_rcf_num const a[], Z3_rcf_num roots[]);
```
@ -82,8 +138,7 @@ function rcf_mk_roots(c: Z3_context, a: Z3_rcf_num[]): { rv: number; roots: Z3_r
When there are multiple arrays which the C API expects to be of the same length, the TS bindings will enforce that this is the case.
### Null pointers
#### Null pointers
Some of the C APIs accept or return null pointers (represented by types whose name end in `_opt`). These are represented in the TS bindings as `| null`. For example, the C declaration
@ -99,8 +154,7 @@ function model_get_const_interp(c: Z3_context, m: Z3_model, a: Z3_func_decl): Z3
}
```
### Async functions
#### Async functions
Certain long-running APIs are not appropriate to call on the main thread. In the TS bindings those APIs are marked as `async` and are automatically called on a different thread. This includes the following APIs:
@ -120,5 +174,4 @@ Certain long-running APIs are not appropriate to call on the main thread. In the
- `Z3_fixedpoint_query_from_lvl`
- `Z3_polynomial_subresultants`
Note that these are not thread-safe, and so only one call can be running at a time. Trying to call a second async API before the first completes will throw.
Note that these are not thread-safe, and so only one call can be running at a time. In contrast to high-level APIs, trying to call a second async API before the first completes will throw.

View file

@ -1,23 +0,0 @@
#!/bin/bash
set -euxo pipefail
export ROOT=$PWD
cd ../../..
export CXXFLAGS="-pthread -s USE_PTHREADS=1 -s DISABLE_EXCEPTION_CATCHING=0"
export LDFLAGS="-s WASM_BIGINT -s -pthread -s USE_PTHREADS=1"
if [ ! -f "build/Makefile" ]; then
emconfigure python scripts/mk_make.py --staticlib --single-threaded
fi
cd build
emmake make -j$(nproc) libz3.a
cd $ROOT
export EM_CACHE=$HOME/.emscripten/
export FNS=$(node scripts/list-exports.js)
export METHODS='["ccall","FS","allocate","UTF8ToString","intArrayFromString","ALLOC_NORMAL"]'
emcc build/async-fns.cc ../../../build/libz3.a --std=c++20 --pre-js src/async-wrapper.js -g2 -pthread -fexceptions -s WASM_BIGINT -s USE_PTHREADS=1 -s PTHREAD_POOL_SIZE=0 -s PTHREAD_POOL_SIZE_STRICT=0 -s MODULARIZE=1 -s 'EXPORT_NAME="initZ3"' -s EXPORTED_RUNTIME_METHODS=$METHODS -s EXPORTED_FUNCTIONS=$FNS -s DISABLE_EXCEPTION_CATCHING=0 -s SAFE_HEAP=0 -s DEMANGLE_SUPPORT=1 -s TOTAL_MEMORY=1GB -I z3/src/api/ -o build/z3-built.js

View file

@ -0,0 +1,206 @@
import { init } from '../../build/node';
import type { Solver, Arith } from '../../build/node';
// solve the "miracle sudoku"
// https://www.youtube.com/watch?v=yKf9aUIxdb4
// most of the interesting stuff is in `solve`
// the process is:
// - parse the board
// - create a Solver
// - create a Z3.Int variable for each square
// - for known cells, add a constraint which says the variable for that cell equals that value
// - add the usual uniqueness constraints
// - add the special "miracle sudoku" constraints
// - call `await solver.check()`
// - if the result is "sat", the board is solvable
// - call `solver.model()` to get a model, i.e. a concrete assignment of variables which satisfies the model
// - for each variable, call `model.evaluate(v)` to recover its value
function parseSudoku(str: string) {
// derive a list of { row, col, val } records, one for each specified position
// from a string like
// ....1..3.
// ..9..5..8
// 8.4..6.25
// ......6..
// ..8..4...
// 12..87...
// 3..9..2..
// .65..8...
// 9........
let cells = [];
let lines = str.trim().split('\n');
if (lines.length !== 9) {
throw new Error(`expected 9 lines, got ${lines.length}`);
}
for (let row = 0; row < 9; ++row) {
let line = lines[row].trim();
if (line.length !== 9) {
throw new Error(`expected line of length 9, got length ${line.length}`);
}
for (let col = 0; col < 9; ++col) {
let char = line[col];
if (char === '.') {
continue;
}
if (char < '1' || char > '9') {
throw new Error(`expected digit or '.', got ${char}`);
}
cells.push({ row, col, value: char.codePointAt(0)! - 48 /* '0' */ });
}
}
return cells;
}
(async () => {
let { Context, em } = await init();
// if you use 'main' as your context name, you won't need to name it in types like Solver
// if you're creating multiple contexts, give them different names
// then the type system will prevent you from mixing them
let Z3 = Context('main');
function addSudokuConstraints(solver: Solver, cells: Arith[][]) {
// the usual constraints:
// every square is between 1 and 9
for (let row of cells) {
for (let cell of row) {
solver.add(cell.ge(1));
solver.add(cell.le(9));
}
}
// values in each row are unique
for (let row of cells) {
solver.add(Z3.Distinct(...row));
}
// values in each column are unique
for (let col = 0; col < 9; ++col) {
solver.add(Z3.Distinct(...cells.map(row => row[col])));
}
// values in each 3x3 subdivision are unique
for (let suprow = 0; suprow < 3; ++suprow) {
for (let supcol = 0; supcol < 3; ++supcol) {
let square = [];
for (let row = 0; row < 3; ++row) {
for (let col = 0; col < 3; ++col) {
square.push(cells[suprow * 3 + row][supcol * 3 + col]);
}
}
solver.add(Z3.Distinct(...square));
}
}
}
function applyOffsets(x: number, y: number, offsets: [number, number][]) {
let out = [];
for (let offset of offsets) {
let rx = x + offset[0];
let ry = y + offset[1];
if (rx >= 0 && rx < 9 && ry >= 0 && ry < 8) {
out.push({ x: rx, y: ry });
}
}
return out;
}
function addMiracleConstraints(s: Solver, cells: Arith[][]) {
// the special "miracle sudoku" constraints
// any two cells separated by a knight's move or a kings move cannot contain the same digit
let knightOffets: [number, number][] = [
[1, -2],
[2, -1],
[2, 1],
[1, 2],
[-1, 2],
[-2, 1],
[-2, -1],
[-1, -2],
];
let kingOffsets: [number, number][] = [
[1, 1],
[1, -1],
[-1, 1],
[-1, -1],
]; // skipping immediately adjacent because those are covered by normal sudoku rules
let allOffets = [...knightOffets, ...kingOffsets];
for (let row = 0; row < 9; ++row) {
for (let col = 0; col < 9; ++col) {
for (let { x, y } of applyOffsets(row, col, allOffets)) {
s.add(cells[row][col].neq(cells[x][y]));
}
}
}
// any two orthogonally adjacent cells cannot contain consecutive digits
let orthoOffsets: [number, number][] = [
[0, 1],
[0, -1],
[1, 0],
[-1, 0],
];
for (let row = 0; row < 9; ++row) {
for (let col = 0; col < 9; ++col) {
for (let { x, y } of applyOffsets(row, col, orthoOffsets)) {
s.add(cells[row][col].sub(cells[x][y]).neq(1));
}
}
}
}
async function solve(str: string) {
let solver = new Z3.Solver();
let cells = Array.from({ length: 9 }, (_, col) => Array.from({ length: 9 }, (_, row) => Z3.Int.const(`c_${row}_${col}`)));
for (let { row, col, value } of parseSudoku(str)) {
solver.add(cells[row][col].eq(value));
}
addSudokuConstraints(solver, cells);
addMiracleConstraints(solver, cells); // remove this line to solve normal sudokus
let start = Date.now();
console.log('starting... this may take a minute or two');
let check = await solver.check();
console.log(`problem was determined to be ${check} in ${Date.now() - start} ms`);
if (check === 'sat') {
let model = solver.model();
let str = '';
for (let row = 0; row < 9; ++row) {
for (let col = 0; col < 9; ++col) {
str += model.eval(cells[row][col]).toString() + (col === 8 ? '' : ' ');
if (col === 2 || col === 5) {
str += ' ';
}
}
str += '\n';
if (row === 2 || row === 5) {
str += '\n';
}
}
console.log(str);
}
}
await solve(`
.........
.........
.........
.........
..1......
......2..
.........
.........
.........
`);
em.PThread.terminateAllThreads();
})().catch(e => {
console.error('error', e);
process.exit(1);
});

View file

@ -1,4 +1,5 @@
import { init } from './build/wrapper';
import process from 'process';
import { init, Z3_error_code } from '../../build/node';
// demonstrates use of the raw API
@ -47,6 +48,15 @@ import { init } from './build/wrapper';
console.log(Z3.query_constructor(ctx, nil_con, 0));
console.log(Z3.query_constructor(ctx, cons_con, 2));
if (Z3.get_error_code(ctx) !== Z3_error_code.Z3_OK) {
throw new Error('something failed: ' + Z3.get_error_msg(ctx, Z3.get_error_code(ctx)));
}
await Z3.eval_smtlib2_string(ctx, '(simplify)');
if (Z3.get_error_code(ctx) === Z3_error_code.Z3_OK) {
throw new Error('expected call to eval_smtlib2_string with invalid argument to fail');
}
console.log('confirming error messages work:', Z3.get_error_msg(ctx, Z3.get_error_code(ctx)));
Z3.dec_ref(ctx, strAst);
Z3.del_context(ctx);

View file

@ -3,24 +3,24 @@
// TypeScript can infer all of them.
// They're just here so readers can see what types things are.
// @ts-ignore we're not going to bother with types for this
import process from 'process';
import { sprintf } from 'sprintf-js';
import type {
Z3_app,
Z3_ast,
Z3_ast_vector,
Z3_config,
Z3_context,
Z3_func_decl,
Z3_func_entry,
Z3_func_interp,
Z3_model,
Z3_solver,
Z3_sort,
Z3_ast,
Z3_app,
Z3_model,
Z3_symbol,
Z3_ast_vector,
Z3_func_decl,
Z3_func_interp,
Z3_func_entry,
} from './build/wrapper';
import { init, Z3_lbool, Z3_ast_kind, Z3_sort_kind, Z3_symbol_kind } from './build/wrapper';
// @ts-ignore we're not going to bother with types for this
import { sprintf } from 'sprintf-js';
} from '../../build/node';
import { init, Z3_ast_kind, Z3_lbool, Z3_sort_kind, Z3_symbol_kind } from '../../build/node';
let printf = (str: string, ...args: unknown[]) => console.log(sprintf(str.replace(/\n$/, ''), ...args));

View file

@ -0,0 +1,7 @@
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
roots: ['<rootDir>/src'],
verbose: true,
};

File diff suppressed because it is too large Load diff

View file

@ -1,27 +1,61 @@
{
"name": "z3-solver",
"keywords": ["Z3", "theorem", "prover", "solver", "satisfiability", "smt", "satisfiability modulo theories"],
"keywords": [
"Z3",
"theorem",
"prover",
"solver",
"satisfiability",
"smt",
"satisfiability modulo theories"
],
"homepage": "https://github.com/Z3Prover/z3/tree/master/src/api/js",
"repository": "github:Z3Prover/z3",
"engines": {
"node": ">=16"
"node": ">=16 <18"
},
"main": "build/wrapper.js",
"types": "build/wrapper.d.ts",
"browser": "build/browser.js",
"main": "build/node.js",
"types": "build/node.d.ts",
"files": [
"build/*.{js,d.ts,wasm}"
],
"scripts": {
"build-ts": "mkdir -p build && node scripts/make-ts-wrapper.js > build/wrapper.ts && tsc",
"build-wasm": "mkdir -p build && node scripts/make-cc-wrapper.js > build/async-fns.cc && ./build-wasm.sh",
"format": "prettier --write --single-quote --arrow-parens avoid --print-width 120 --trailing-comma all '{,src/,scripts/}*.{js,ts}'",
"test": "node test-ts-api.js"
"build:ts": "run-s -l build:ts:generate build:ts:tsc",
"build:ts:tsc": "tsc --pretty --project tsconfig.build.json ",
"build:ts:generate": "ts-node --transpileOnly scripts/make-ts-wrapper.ts src/low-level/wrapper.__GENERATED__.ts src/low-level/types.__GENERATED__.ts",
"build:wasm": "ts-node --transpileOnly ./scripts/build-wasm.ts",
"clean": "rimraf build 'src/**/*.__GENERATED__.*'",
"lint": "prettier -c '{,src/,scripts/,examples}*.{js,ts}'",
"format": "prettier --write '{,src/,scripts/}*.{js,ts}'",
"test": "jest",
"docs": "typedoc",
"check-engine": "check-engine"
},
"contributors": [
"Kevin Gibbons <bakkot@gmail.com>",
"Nikolaj Bjorner",
"Olaf Tomalka <olaf@tomalka.me>"
],
"devDependencies": {
"@types/jest": "^27.5.1",
"@types/node": "^17.0.8",
"@types/prettier": "^2.6.1",
"@types/sprintf-js": "^1.1.2",
"check-engine": "^1.10.1",
"iter-tools": "^7.3.1",
"jest": "^28.1.0",
"npm-run-all": "^4.1.5",
"prettier": "^2.5.1",
"rimraf": "^3.0.2",
"sprintf-js": "^1.1.2",
"ts-jest": "^28.0.3",
"ts-node": "^10.8.0",
"typedoc": "^0.22.17",
"typescript": "^4.5.4"
},
"license": "MIT"
"license": "MIT",
"dependencies": {
"async-mutex": "^0.3.2"
}
}

View file

@ -1,8 +1,6 @@
'use strict';
// things which you probably want to do off-thread
// from https://github.com/Z3Prover/z3/issues/5746#issuecomment-1006289146
module.exports = [
export const asyncFuncs = [
'Z3_eval_smtlib2_string',
'Z3_simplify',
'Z3_simplify_ex',

View file

@ -0,0 +1,77 @@
import assert from 'assert';
import { SpawnOptions, spawnSync as originalSpawnSync } from 'child_process';
import fs, { existsSync } from 'fs';
import os from 'os';
import path from 'path';
import process from 'process';
import { asyncFuncs } from './async-fns';
import { makeCCWrapper } from './make-cc-wrapper';
import { functions } from './parse-api';
console.log('--- Building WASM');
const SWAP_OPTS: SpawnOptions = {
shell: true,
stdio: 'inherit',
env: {
...process.env,
CXXFLAGS: '-pthread -s USE_PTHREADS=1 -s DISABLE_EXCEPTION_CATCHING=0',
LDFLAGS: '-s WASM_BIGINT -s -pthread -s USE_PTHREADS=1',
FPMATH_ENABLED: 'False', // Until Safari supports WASM SSE, we have to disable fast FP support
// TODO(ritave): Setting EM_CACHE breaks compiling on M1 MacBook
//EM_CACHE: path.join(os.homedir(), '.emscripten/'),
},
};
function spawnSync(command: string, opts: SpawnOptions = {}) {
console.log(`- ${command}`);
// TODO(ritave): Create a splitter that keeps track of quoted strings
const [cmd, ...args] = command.split(' ');
const { error, ...rest } = originalSpawnSync(cmd, args, { ...SWAP_OPTS, ...opts });
if (error !== undefined || rest.status !== 0) {
if (error !== undefined) {
console.error(error.message);
} else {
console.error(`Process exited with status ${rest.status}`);
}
process.exit(1);
}
return rest;
}
function exportedFuncs(): string[] {
const extras = ['_set_throwy_error_handler', '_set_noop_error_handler', ...asyncFuncs.map(f => '_async_' + f)];
// TODO(ritave): This variable is unused in original script, find out if it's important
const fns: any[] = (functions as any[]).filter(f => !asyncFuncs.includes(f.name));
return [...extras, ...(functions as any[]).map(f => '_' + f.name)];
}
assert(fs.existsSync('./package.json'), 'Not in the root directory of js api');
const z3RootDir = path.join(process.cwd(), '../../../');
// TODO(ritave): Detect if it's in the configuration we need
if (!existsSync(path.join(z3RootDir, 'build/Makefile'))) {
spawnSync('emconfigure python scripts/mk_make.py --staticlib --single-threaded --arm64=false', {
cwd: z3RootDir,
});
}
spawnSync(`emmake make -j${os.cpus().length} libz3.a`, { cwd: path.join(z3RootDir, 'build') });
const ccWrapperPath = 'build/async-fns.cc';
console.log(`- Building ${ccWrapperPath}`);
fs.mkdirSync(path.dirname(ccWrapperPath), { recursive: true });
fs.writeFileSync(ccWrapperPath, makeCCWrapper());
const fns = JSON.stringify(exportedFuncs());
const methods = '["ccall","FS","allocate","UTF8ToString","intArrayFromString","ALLOC_NORMAL"]';
const libz3a = path.normalize('../../../build/libz3.a');
spawnSync(
`emcc build/async-fns.cc ${libz3a} --std=c++20 --pre-js src/low-level/async-wrapper.js -g2 -pthread -fexceptions -s WASM_BIGINT -s USE_PTHREADS=1 -s PTHREAD_POOL_SIZE=0 -s PTHREAD_POOL_SIZE_STRICT=0 -s MODULARIZE=1 -s 'EXPORT_NAME="initZ3"' -s EXPORTED_RUNTIME_METHODS=${methods} -s EXPORTED_FUNCTIONS=${fns} -s DISABLE_EXCEPTION_CATCHING=0 -s SAFE_HEAP=0 -s DEMANGLE_SUPPORT=1 -s TOTAL_MEMORY=1GB -I z3/src/api/ -o build/z3-built.js`,
);
fs.rmSync(ccWrapperPath);
console.log('--- WASM build finished');

View file

@ -1,11 +0,0 @@
'use strict';
// this is called by build.sh to generate the names of the bindings to export
let { functions } = require('./parse-api.js');
let asyncFns = require('./async-fns.js');
let extras = asyncFns.map(f => '_async_' + f);
let fns = functions.filter(f => !asyncFns.includes(f.name));
console.log(JSON.stringify([...extras, ...functions.map(f => '_' + f.name)]));

View file

@ -1,82 +0,0 @@
'use strict';
// generates c wrappers with off-thread versions of specified functions
let path = require('path');
let { functions } = require('./parse-api.js');
let asyncFns = require('./async-fns.js');
let wrappers = [];
for (let fnName of asyncFns) {
let fn = functions.find(f => f.name === fnName);
if (fn == null) {
throw new Error(`could not find definition for ${fnName}`);
}
let wrapper;
if (fn.cRet === 'Z3_string') {
wrapper = `wrapper_str`;
} else if (['int', 'unsigned', 'void'].includes(fn.cRet) || fn.cRet.startsWith('Z3_')) {
wrapper = `wrapper`;
} else {
throw new Error(`async function with unknown return type ${fn.cRet}`);
}
wrappers.push(
`
extern "C" void async_${fn.name}(${fn.params
.map(p => `${p.isConst ? 'const ' : ''}${p.cType}${p.isPtr ? '*' : ''} ${p.name}${p.isArray ? '[]' : ''}`)
.join(', ')}) {
${wrapper}<decltype(&${fn.name}), &${fn.name}>(${fn.params.map(p => `${p.name}`).join(', ')});
}
`.trim(),
);
}
console.log(`// THIS FILE IS AUTOMATICALLY GENERATED BY ${path.basename(__filename)}
// DO NOT EDIT IT BY HAND
#include <thread>
#include <emscripten.h>
#include "../../z3.h"
template<typename Fn, Fn fn, typename... Args>
void wrapper(Args&&... args) {
std::thread t([...args = std::forward<Args>(args)] {
try {
auto result = fn(args...);
MAIN_THREAD_ASYNC_EM_ASM({
resolve_async($0);
}, result);
} catch (...) {
MAIN_THREAD_ASYNC_EM_ASM({
reject_async('failed with unknown exception');
});
throw;
}
});
t.detach();
}
template<typename Fn, Fn fn, typename... Args>
void wrapper_str(Args&&... args) {
std::thread t([...args = std::forward<Args>(args)] {
try {
auto result = fn(args...);
MAIN_THREAD_ASYNC_EM_ASM({
resolve_async(UTF8ToString($0));
}, result);
} catch (...) {
MAIN_THREAD_ASYNC_EM_ASM({
reject_async('failed with unknown exception');
});
throw;
}
});
t.detach();
}
${wrappers.join('\n\n')}`);

View file

@ -0,0 +1,119 @@
// generates c wrappers with off-thread versions of specified functions
import path from 'path';
import { asyncFuncs } from './async-fns';
import { functions } from './parse-api';
export function makeCCWrapper() {
let wrappers = [];
for (let fnName of asyncFuncs) {
let fn = functions.find(f => f.name === fnName);
if (fn == null) {
throw new Error(`could not find definition for ${fnName}`);
}
let wrapper;
if (fn.cRet === 'Z3_string') {
wrapper = `wrapper_str`;
} else if (['int', 'unsigned', 'void'].includes(fn.cRet) || fn.cRet.startsWith('Z3_')) {
wrapper = `wrapper`;
} else {
throw new Error(`async function with unknown return type ${fn.cRet}`);
}
wrappers.push(
`
extern "C" void async_${fn.name}(${fn.params
.map(p => `${p.isConst ? 'const ' : ''}${p.cType}${p.isPtr ? '*' : ''} ${p.name}${p.isArray ? '[]' : ''}`)
.join(', ')}) {
${wrapper}<decltype(&${fn.name}), &${fn.name}>(${fn.params.map(p => `${p.name}`).join(', ')});
}
`.trim(),
);
}
return `// THIS FILE IS AUTOMATICALLY GENERATED BY ${path.basename(__filename)}
// DO NOT EDIT IT BY HAND
#include <thread>
#include <emscripten.h>
#include "../../z3.h"
template<typename Fn, Fn fn, typename... Args>
void wrapper(Args&&... args) {
std::thread t([...args = std::forward<Args>(args)] {
try {
auto result = fn(args...);
MAIN_THREAD_ASYNC_EM_ASM({
resolve_async($0);
}, result);
} catch (std::exception& e) {
MAIN_THREAD_ASYNC_EM_ASM({
reject_async(new Error(UTF8ToString($0)));
}, e.what());
} catch (...) {
MAIN_THREAD_ASYNC_EM_ASM({
reject_async('failed with unknown exception');
});
}
});
t.detach();
}
template<typename Fn, Fn fn, typename... Args>
void wrapper_str(Args&&... args) {
std::thread t([...args = std::forward<Args>(args)] {
try {
auto result = fn(args...);
MAIN_THREAD_ASYNC_EM_ASM({
resolve_async(UTF8ToString($0));
}, result);
} catch (std::exception& e) {
MAIN_THREAD_ASYNC_EM_ASM({
reject_async(new Error(UTF8ToString($0)));
}, e.what());
} catch (...) {
MAIN_THREAD_ASYNC_EM_ASM({
reject_async(new Error('failed with unknown exception'));
});
}
});
t.detach();
}
class Z3Exception : public std::exception {
public:
const std::string m_msg;
Z3Exception(const std::string& msg) : m_msg(msg) {}
virtual const char* what() const throw () {
return m_msg.c_str();
}
};
void throwy_error_handler(Z3_context ctx, Z3_error_code c) {
throw Z3Exception(Z3_get_error_msg(ctx, c));
}
void noop_error_handler(Z3_context ctx, Z3_error_code c) {
// pass
}
extern "C" void set_throwy_error_handler(Z3_context ctx) {
Z3_set_error_handler(ctx, throwy_error_handler);
}
extern "C" void set_noop_error_handler(Z3_context ctx) {
Z3_set_error_handler(ctx, noop_error_handler);
}
${wrappers.join('\n\n')}
`;
}
if (require.main === module) {
console.log(makeCCWrapper());
}

View file

@ -1,422 +0,0 @@
'use strict';
let path = require('path');
let prettier = require('prettier');
let { primitiveTypes, types, enums, functions } = require('./parse-api.js');
let asyncFns = require('./async-fns.js');
let subtypes = {
__proto__: null,
Z3_sort: 'Z3_ast',
Z3_func_decl: 'Z3_ast',
};
let makePointerType = t =>
`export type ${t} = ` + (t in subtypes ? `Subpointer<'${t}', '${subtypes[t]}'>;` : `Pointer<'${t}'>;`);
// this supports a up to 6 out intergers/pointers
// or up to 3 out int64s
const BYTES_TO_ALLOCATE_FOR_OUT_PARAMS = 24;
function toEmType(type) {
if (type in primitiveTypes) {
type = primitiveTypes[type];
}
if (['boolean', 'number', 'string', 'bigint', 'void'].includes(type)) {
return type;
}
if (type.startsWith('Z3_')) {
return 'number';
}
throw new Error(`unknown parameter type ${type}`);
}
function isZ3PointerType(type) {
return type.startsWith('Z3_');
}
function toEm(p) {
if (typeof p === 'string') {
// we've already set this, e.g. by replacing it with an expression
return p;
}
let { type } = p;
if (p.kind === 'out') {
throw new Error(`unknown out parameter type ${JSON.stringify(p)}`);
}
if (p.isArray) {
if (isZ3PointerType(type) || type === 'unsigned' || type === 'int') {
// this works for nullables also because null coerces to 0
return `intArrayToByteArr(${p.name} as unknown as number[])`;
} else if (type === 'boolean') {
return `boolArrayToByteArr(${p.name})`;
} else {
throw new Error(`only know how to deal with arrays of int/bool (got ${type})`);
}
}
if (type in primitiveTypes) {
type = primitiveTypes[type];
}
if (['boolean', 'number', 'bigint', 'string'].includes(type)) {
return p.name;
}
if (type.startsWith('Z3_')) {
return p.name;
}
throw new Error(`unknown parameter type ${JSON.stringify(p)}`);
}
let isInParam = p => ['in', 'in_array'].includes(p.kind);
function wrapFunction(fn) {
let inParams = fn.params.filter(isInParam);
let outParams = fn.params.map((p, idx) => ({ ...p, idx })).filter(p => !isInParam(p));
// we'll figure out how to deal with these cases later
let unknownInParam = inParams.find(
p =>
p.isPtr ||
p.type === 'Z3_char_ptr' ||
(p.isArray && !(isZ3PointerType(p.type) || p.type === 'unsigned' || p.type === 'int' || p.type === 'boolean')),
);
if (unknownInParam) {
console.error(`skipping ${fn.name} - unknown in parameter ${JSON.stringify(unknownInParam)}`);
return null;
}
if (fn.ret === 'Z3_char_ptr') {
console.error(`skipping ${fn.name} - returns a string or char pointer`);
return null;
}
// console.error(fn.name);
let isAsync = asyncFns.includes(fn.name);
let trivial =
!['string', 'boolean'].includes(fn.ret) &&
!fn.nullableRet &&
outParams.length === 0 &&
!inParams.some(p => p.type === 'string' || p.isArray || p.nullable);
let name = fn.name.startsWith('Z3_') ? fn.name.substring(3) : fn.name;
let params = inParams.map(p => {
let type = p.type;
if (p.isArray && p.nullable) {
type = `(${type} | null)[]`;
} else if (p.isArray) {
type = `${type}[]`;
} else if (p.nullable) {
type = `${type} | null`;
}
return `${p.name}: ${type}`;
});
if (trivial && isAsync) {
// i.e. and async
return `${name}: function (${params.join(', ')}): Promise<${fn.ret}> {
return Mod.async_call(Mod._async_${fn.name}, ${fn.params.map(toEm).join(', ')});
}`;
}
if (trivial) {
return `${name}: Mod._${fn.name} as ((${params.join(', ')}) => ${fn.ret})`;
}
// otherwise fall back to ccall
let ctypes = fn.params.map(p =>
p.kind === 'in_array' ? 'array' : p.kind === 'out_array' ? 'number' : p.isPtr ? 'number' : toEmType(p.type),
);
let prefix = '';
let infix = '';
let rv = 'ret';
let suffix = '';
let args = fn.params;
let arrayLengthParams = new Map();
for (let p of inParams) {
if (p.nullable && !p.isArray) {
// this would be easy to implement - just map null to 0 - but nothing actually uses nullable non-array input parameters, so we can't ensure we've done it right
console.error(`skipping ${fn.name} - nullable input parameter`);
return null;
}
if (!p.isArray) {
continue;
}
let { sizeIndex } = p;
if (arrayLengthParams.has(sizeIndex)) {
let otherParam = arrayLengthParams.get(sizeIndex);
prefix += `
if (${otherParam}.length !== ${p.name}.length) {
throw new TypeError(\`${otherParam} and ${p.name} must be the same length (got \${${otherParam}.length} and \{${p.name}.length})\`);
}
`.trim();
continue;
}
arrayLengthParams.set(sizeIndex, p.name);
let sizeParam = fn.params[sizeIndex];
if (!(sizeParam.kind === 'in' && sizeParam.type === 'unsigned' && !sizeParam.isPtr && !sizeParam.isArray)) {
throw new Error(
`size index is not unsigned int (for fn ${fn.name} parameter ${sizeIndex} got ${sizeParam.type})`,
);
}
args[sizeIndex] = `${p.name}.length`;
params[sizeIndex] = null;
}
let returnType = fn.ret;
let cReturnType = toEmType(fn.ret);
if (outParams.length > 0) {
let mapped = [];
let memIdx = 0; // offset from `outAddress` where the data should get written, in units of 4 bytes
for (let outParam of outParams) {
if (outParam.isArray) {
if (isZ3PointerType(outParam.type) || outParam.type === 'unsigned') {
let { sizeIndex } = outParam;
let count;
if (arrayLengthParams.has(sizeIndex)) {
// i.e. this is also the length of an input array
count = args[sizeIndex];
} else {
let sizeParam = fn.params[sizeIndex];
if (!(sizeParam.kind === 'in' && sizeParam.type === 'unsigned' && !sizeParam.isPtr && !sizeParam.isArray)) {
throw new Error(
`size index is not unsigned int (for fn ${fn.name} parameter ${sizeIndex} got ${sizeParam.type})`,
);
}
count = sizeParam.name;
}
let outArrayAddress = `outArray_${outParam.name}`;
prefix += `
let ${outArrayAddress} = Mod._malloc(4 * ${count});
try {
`.trim();
suffix =
`
} finally {
Mod._free(${outArrayAddress});
}
`.trim() + suffix;
args[outParam.idx] = outArrayAddress;
mapped.push({
name: outParam.name,
read:
`readUintArray(${outArrayAddress}, ${count})` +
(outParam.type === 'unsigned' ? '' : `as unknown as ${outParam.type}[]`),
type: `${outParam.type}[]`,
});
} else {
console.error(`skipping ${fn.name} - out array of ${outParam.type}`);
return null;
}
} else if (outParam.isPtr) {
function setArg() {
args[outParam.idx] = memIdx === 0 ? 'outAddress' : `outAddress + ${memIdx * 4}`;
}
let read, type;
if (outParam.type === 'string') {
read = `Mod.UTF8ToString(getOutUint(${memIdx}))`;
setArg();
++memIdx;
} else if (isZ3PointerType(outParam.type)) {
read = `getOutUint(${memIdx}) as unknown as ${outParam.type}`;
setArg();
++memIdx;
} else if (outParam.type === 'unsigned') {
read = `getOutUint(${memIdx})`;
setArg();
++memIdx;
} else if (outParam.type === 'int') {
read = `getOutInt(${memIdx})`;
setArg();
++memIdx;
} else if (outParam.type === 'uint64_t') {
if (memIdx % 2 === 1) {
++memIdx;
}
read = `getOutUint64(${memIdx / 2})`;
setArg();
memIdx += 2;
} else if (outParam.type === 'int64_t') {
if (memIdx % 2 === 1) {
++memIdx;
}
read = `getOutInt64(${memIdx / 2})`;
setArg();
memIdx += 2;
} else {
console.error(`skipping ${fn.name} - unknown out parameter type ${outParam.type}`);
return null;
}
if (memIdx > Math.floor(BYTES_TO_ALLOCATE_FOR_OUT_PARAMS / 4)) {
// prettier-ignore
console.error(`skipping ${fn.name} - out parameter sizes sum to ${memIdx * 4}, which is > ${BYTES_TO_ALLOCATE_FOR_OUT_PARAMS}`);
return null;
}
mapped.push({
name: outParam.name,
read,
type: outParam.type,
});
} else {
console.error(`skipping ${fn.name} - out param is neither pointer nor array`);
return null;
}
}
let ignoreReturn = fn.ret === 'boolean' || fn.ret === 'void';
if (outParams.length === 1) {
let outParam = mapped[0];
if (ignoreReturn) {
returnType = outParam.type;
rv = outParam.read;
} else {
returnType = `{ rv: ${fn.ret}, ${outParam.name} : ${outParam.type} }`;
rv = `{ rv: ret, ${outParam.name} : ${outParam.read} }`;
}
} else {
if (ignoreReturn) {
returnType = `{ ${mapped.map(p => `${p.name} : ${p.type}`).join(', ')} }`;
rv = `{ ${mapped.map(p => `${p.name}: ${p.read}`).join(', ')} }`;
} else {
returnType = `{ rv: ${fn.ret}, ${mapped.map(p => `${p.name} : ${p.type}`).join(', ')} }`;
rv = `{ rv: ret, ${mapped.map(p => `${p.name}: ${p.read}`).join(', ')} }`;
}
}
if (fn.ret === 'boolean') {
// assume the boolean indicates success
infix += `
if (!ret) {
return null;
}
`.trim();
cReturnType = 'boolean';
returnType += ' | null';
} else if (fn.ret === 'void') {
cReturnType = 'void';
} else if (isZ3PointerType(fn.ret) || fn.ret === 'unsigned') {
cReturnType = 'number';
} else {
console.error(`skipping ${fn.name} - out parameter for function which returns non-boolean`);
return null;
}
}
if (fn.nullableRet) {
returnType += ' | null';
infix += `
if (ret === 0) {
return null;
}
`.trim();
}
if (isAsync) {
}
// prettier-ignore
let invocation = `Mod.ccall('${isAsync ? 'async_' : ''}${fn.name}', '${cReturnType}', ${JSON.stringify(ctypes)}, [${args.map(toEm).join(', ')}])`;
if (isAsync) {
invocation = `await Mod.async_call(() => ${invocation})`;
returnType = `Promise<${returnType}>`;
}
let out = `${name}: ${isAsync ? 'async' : ''} function(${params.filter(p => p != null).join(', ')}): ${returnType} {
${prefix}`;
if (infix === '' && suffix === '' && rv === 'ret') {
out += `return ${invocation};`;
} else {
out += `
let ret = ${invocation};
${infix}return ${rv};${suffix}
`.trim();
}
out += '}';
return out;
}
function wrapEnum(name, values) {
let enumEntries = Object.entries(values);
return `export enum ${name} {
${enumEntries.map(([k, v], i) => k + (v === (enumEntries[i - 1]?.[1] ?? -1) + 1 ? '' : ` = ${v}`) + ',').join('\n')}
};`;
}
function getValidOutArrayIndexes(size) {
return Array.from({ length: Math.floor(BYTES_TO_ALLOCATE_FOR_OUT_PARAMS / size) }, (_, i) => i).join(' | ');
}
let out = `
// THIS FILE IS AUTOMATICALLY GENERATED BY ${path.basename(__filename)}
// DO NOT EDIT IT BY HAND
// @ts-ignore no-implicit-any
import initModule = require('./z3-built.js');
interface Pointer<T extends string> extends Number {
readonly __typeName: T;
}
interface Subpointer<T extends string, S extends string> extends Pointer<S> {
readonly __typeName2: T;
}
${Object.entries(primitiveTypes)
.filter(e => e[0] !== 'void')
.map(e => `type ${e[0]} = ${e[1]};`)
.join('\n')}
${Object.keys(types)
.filter(k => k.startsWith('Z3'))
.map(makePointerType)
.join('\n')}
${Object.entries(enums)
.map(e => wrapEnum(e[0], e[1]))
.join('\n\n')}
export async function init() {
let Mod = await initModule();
// this works for both signed and unsigned, because JS will wrap for you when constructing the Uint32Array
function intArrayToByteArr(ints: number[]) {
return new Uint8Array((new Uint32Array(ints)).buffer);
}
function boolArrayToByteArr(bools: boolean[]) {
return bools.map(b => b ? 1 : 0);
}
function readUintArray(address: number, count: number) {
return Array.from(new Uint32Array(Mod.HEAPU32.buffer, address, count));
}
let outAddress = Mod._malloc(${BYTES_TO_ALLOCATE_FOR_OUT_PARAMS});
let outUintArray = (new Uint32Array(Mod.HEAPU32.buffer, outAddress, 4));
let getOutUint = (i: ${getValidOutArrayIndexes(4)}) => outUintArray[i];
let outIntArray = (new Int32Array(Mod.HEAPU32.buffer, outAddress, 4));
let getOutInt = (i: ${getValidOutArrayIndexes(4)}) => outIntArray[i];
let outUint64Array = (new BigUint64Array(Mod.HEAPU32.buffer, outAddress, 2));
let getOutUint64 = (i: ${getValidOutArrayIndexes(8)}) => outUint64Array[i];
let outInt64Array = (new BigInt64Array(Mod.HEAPU32.buffer, outAddress, 2));
let getOutInt64 = (i: ${getValidOutArrayIndexes(8)}) => outInt64Array[i];
return {
em: Mod,
Z3: {
${functions
.map(wrapFunction)
.filter(f => f != null)
.join(',\n')}
}
};
}
`;
console.log(prettier.format(out, { singleQuote: true, parser: 'typescript' }));

View file

@ -0,0 +1,468 @@
import assert from 'assert';
import fs from 'fs';
import path from 'path';
import prettier from 'prettier';
import { asyncFuncs } from './async-fns';
import { enums, Func, FuncParam, functions, primitiveTypes, types } from './parse-api';
assert(process.argv.length === 4, `Usage: ${process.argv[0]} ${process.argv[1]} wrapperFilePath typesFilePath`);
const wrapperFilePath = process.argv[2];
const typesFilePath = process.argv[3];
function makeTsWrapper() {
const subtypes = {
__proto__: null,
Z3_sort: 'Z3_ast',
Z3_func_decl: 'Z3_ast',
} as unknown as Record<string, string>;
const makePointerType = (t: string) =>
`export type ${t} = ` + (t in subtypes ? `Subpointer<'${t}', '${subtypes[t]}'>;` : `Pointer<'${t}'>;`);
// this supports a up to 6 out integers/pointers
// or up to 3 out int64s
const BYTES_TO_ALLOCATE_FOR_OUT_PARAMS = 24;
const CUSTOM_IMPLEMENTATIONS = ['Z3_mk_context', 'Z3_mk_context_rc'];
function toEmType(type: string) {
if (type in primitiveTypes) {
type = primitiveTypes[type];
}
if (['boolean', 'number', 'string', 'bigint', 'void'].includes(type)) {
return type;
}
if (type.startsWith('Z3_')) {
return 'number';
}
throw new Error(`unknown parameter type ${type}`);
}
function isZ3PointerType(type: string) {
return type.startsWith('Z3_');
}
function toEm(p: string | FuncParam) {
if (typeof p === 'string') {
// we've already set this, e.g. by replacing it with an expression
return p;
}
let { type } = p;
if (p.kind === 'out') {
throw new Error(`unknown out parameter type ${JSON.stringify(p)}`);
}
if (p.isArray) {
if (isZ3PointerType(type) || type === 'unsigned' || type === 'int') {
// this works for nullables also because null coerces to 0
return `intArrayToByteArr(${p.name} as unknown as number[])`;
} else if (type === 'boolean') {
return `boolArrayToByteArr(${p.name})`;
} else {
throw new Error(`only know how to deal with arrays of int/bool (got ${type})`);
}
}
if (type in primitiveTypes) {
type = primitiveTypes[type];
}
if (['boolean', 'number', 'bigint', 'string'].includes(type)) {
return p.name;
}
if (type.startsWith('Z3_')) {
return p.name;
}
throw new Error(`unknown parameter type ${JSON.stringify(p)}`);
}
const isInParam = (p: FuncParam) => p.kind !== undefined && ['in', 'in_array'].includes(p.kind);
function wrapFunction(fn: Func) {
if (CUSTOM_IMPLEMENTATIONS.includes(fn.name)) {
return null;
}
let inParams = fn.params.filter(isInParam);
let outParams = fn.params.map((p, idx) => ({ ...p, idx })).filter(p => !isInParam(p));
// we'll figure out how to deal with these cases later
let unknownInParam = inParams.find(
p =>
p.isPtr ||
p.type === 'Z3_char_ptr' ||
(p.isArray && !(isZ3PointerType(p.type) || p.type === 'unsigned' || p.type === 'int' || p.type === 'boolean')),
);
if (unknownInParam) {
console.error(`skipping ${fn.name} - unknown in parameter ${JSON.stringify(unknownInParam)}`);
return null;
}
if (fn.ret === 'Z3_char_ptr') {
console.error(`skipping ${fn.name} - returns a string or char pointer`);
return null;
}
// console.error(fn.name);
let isAsync = asyncFuncs.includes(fn.name);
let trivial =
!['string', 'boolean'].includes(fn.ret) &&
!fn.nullableRet &&
outParams.length === 0 &&
!inParams.some(p => p.type === 'string' || p.isArray || p.nullable);
let name = fn.name.startsWith('Z3_') ? fn.name.substring(3) : fn.name;
const params: (string | null)[] = inParams.map(p => {
let type = p.type;
if (p.isArray && p.nullable) {
type = `(${type} | null)[]`;
} else if (p.isArray) {
type = `${type}[]`;
} else if (p.nullable) {
type = `${type} | null`;
}
return `${p.name}: ${type}`;
});
if (trivial && isAsync) {
// i.e. and async
return `${name}: function (${params.join(', ')}): Promise<${fn.ret}> {
return Mod.async_call(Mod._async_${fn.name}, ${fn.params.map(toEm).join(', ')});
}`;
}
if (trivial) {
return `${name}: Mod._${fn.name} as ((${params.join(', ')}) => ${fn.ret})`;
}
// otherwise fall back to ccall
const ctypes = fn.params.map(p =>
p.kind === 'in_array' ? 'array' : p.kind === 'out_array' ? 'number' : p.isPtr ? 'number' : toEmType(p.type),
);
let prefix = '';
let infix = '';
let rv = 'ret';
let suffix = '';
const args: (string | FuncParam)[] = fn.params;
let arrayLengthParams = new Map();
for (let p of inParams) {
if (p.nullable && !p.isArray) {
// this would be easy to implement - just map null to 0 - but nothing actually uses nullable non-array input parameters, so we can't ensure we've done it right
console.error(`skipping ${fn.name} - nullable input parameter`);
return null;
}
if (!p.isArray) {
continue;
}
let { sizeIndex } = p;
assert(sizeIndex !== undefined);
if (arrayLengthParams.has(sizeIndex)) {
let otherParam = arrayLengthParams.get(sizeIndex);
prefix += `
if (${otherParam}.length !== ${p.name}.length) {
throw new TypeError(\`${otherParam} and ${p.name} must be the same length (got \${${otherParam}.length} and \{${p.name}.length})\`);
}
`.trim();
continue;
}
arrayLengthParams.set(sizeIndex, p.name);
const sizeParam = fn.params[sizeIndex];
if (!(sizeParam.kind === 'in' && sizeParam.type === 'unsigned' && !sizeParam.isPtr && !sizeParam.isArray)) {
throw new Error(
`size index is not unsigned int (for fn ${fn.name} parameter ${sizeIndex} got ${sizeParam.type})`,
);
}
args[sizeIndex] = `${p.name}.length`;
params[sizeIndex] = null;
}
let returnType = fn.ret;
let cReturnType = toEmType(fn.ret);
if (outParams.length > 0) {
let mapped = [];
let memIdx = 0; // offset from `outAddress` where the data should get written, in units of 4 bytes
for (let outParam of outParams) {
if (outParam.isArray) {
if (isZ3PointerType(outParam.type) || outParam.type === 'unsigned') {
let { sizeIndex } = outParam;
assert(sizeIndex !== undefined);
let count;
if (arrayLengthParams.has(sizeIndex)) {
// i.e. this is also the length of an input array
count = args[sizeIndex];
} else {
let sizeParam = fn.params[sizeIndex];
if (
!(sizeParam.kind === 'in' && sizeParam.type === 'unsigned' && !sizeParam.isPtr && !sizeParam.isArray)
) {
throw new Error(
`size index is not unsigned int (for fn ${fn.name} parameter ${sizeIndex} got ${sizeParam.type})`,
);
}
count = sizeParam.name;
}
let outArrayAddress = `outArray_${outParam.name}`;
prefix += `
let ${outArrayAddress} = Mod._malloc(4 * ${count});
try {
`.trim();
suffix =
`
} finally {
Mod._free(${outArrayAddress});
}
`.trim() + suffix;
args[outParam.idx] = outArrayAddress;
mapped.push({
name: outParam.name,
read:
`readUintArray(${outArrayAddress}, ${count})` +
(outParam.type === 'unsigned' ? '' : `as unknown as ${outParam.type}[]`),
type: `${outParam.type}[]`,
});
} else {
console.error(`skipping ${fn.name} - out array of ${outParam.type}`);
return null;
}
} else if (outParam.isPtr) {
function setArg() {
args[outParam.idx] = memIdx === 0 ? 'outAddress' : `outAddress + ${memIdx * 4}`;
}
let read, type;
if (outParam.type === 'string') {
read = `Mod.UTF8ToString(getOutUint(${memIdx}))`;
setArg();
++memIdx;
} else if (isZ3PointerType(outParam.type)) {
read = `getOutUint(${memIdx}) as unknown as ${outParam.type}`;
setArg();
++memIdx;
} else if (outParam.type === 'unsigned') {
read = `getOutUint(${memIdx})`;
setArg();
++memIdx;
} else if (outParam.type === 'int') {
read = `getOutInt(${memIdx})`;
setArg();
++memIdx;
} else if (outParam.type === 'uint64_t') {
if (memIdx % 2 === 1) {
++memIdx;
}
read = `getOutUint64(${memIdx / 2})`;
setArg();
memIdx += 2;
} else if (outParam.type === 'int64_t') {
if (memIdx % 2 === 1) {
++memIdx;
}
read = `getOutInt64(${memIdx / 2})`;
setArg();
memIdx += 2;
} else {
console.error(`skipping ${fn.name} - unknown out parameter type ${outParam.type}`);
return null;
}
if (memIdx > Math.floor(BYTES_TO_ALLOCATE_FOR_OUT_PARAMS / 4)) {
// prettier-ignore
console.error(`skipping ${fn.name} - out parameter sizes sum to ${memIdx * 4}, which is > ${BYTES_TO_ALLOCATE_FOR_OUT_PARAMS}`);
return null;
}
mapped.push({
name: outParam.name,
read,
type: outParam.type,
});
} else {
console.error(`skipping ${fn.name} - out param is neither pointer nor array`);
return null;
}
}
let ignoreReturn = fn.ret === 'boolean' || fn.ret === 'void';
if (outParams.length === 1) {
let outParam = mapped[0];
if (ignoreReturn) {
returnType = outParam.type;
rv = outParam.read;
} else {
returnType = `{ rv: ${fn.ret}, ${outParam.name} : ${outParam.type} }`;
rv = `{ rv: ret, ${outParam.name} : ${outParam.read} }`;
}
} else {
if (ignoreReturn) {
returnType = `{ ${mapped.map(p => `${p.name} : ${p.type}`).join(', ')} }`;
rv = `{ ${mapped.map(p => `${p.name}: ${p.read}`).join(', ')} }`;
} else {
returnType = `{ rv: ${fn.ret}, ${mapped.map(p => `${p.name} : ${p.type}`).join(', ')} }`;
rv = `{ rv: ret, ${mapped.map(p => `${p.name}: ${p.read}`).join(', ')} }`;
}
}
if (fn.ret === 'boolean') {
// assume the boolean indicates success
infix += `
if (!ret) {
return null;
}
`.trim();
cReturnType = 'boolean';
returnType += ' | null';
} else if (fn.ret === 'void') {
cReturnType = 'void';
} else if (isZ3PointerType(fn.ret) || fn.ret === 'unsigned') {
cReturnType = 'number';
} else {
console.error(`skipping ${fn.name} - out parameter for function which returns non-boolean`);
return null;
}
}
if (fn.nullableRet) {
returnType += ' | null';
infix += `
if (ret === 0) {
return null;
}
`.trim();
}
// prettier-ignore
let invocation = `Mod.ccall('${isAsync ? 'async_' : ''}${fn.name}', '${cReturnType}', ${JSON.stringify(ctypes)}, [${args.map(toEm).join(', ')}])`;
if (isAsync) {
invocation = `await Mod.async_call(() => ${invocation})`;
returnType = `Promise<${returnType}>`;
}
let out = `${name}: ${isAsync ? 'async' : ''} function(${params.filter(p => p != null).join(', ')}): ${returnType} {
${prefix}`;
if (infix === '' && suffix === '' && rv === 'ret') {
out += `return ${invocation};`;
} else {
out += `
let ret = ${invocation};
${infix}return ${rv};${suffix}
`.trim();
}
out += '}';
return out;
}
function wrapEnum(name: string, values: Record<string, number>) {
let enumEntries = Object.entries(values);
return `export enum ${name} {
${enumEntries.map(([k, v], i) => k + (v === (enumEntries[i - 1]?.[1] ?? -1) + 1 ? '' : ` = ${v}`) + ',').join('\n')}
};`;
}
function getValidOutArrayIndexes(size: number) {
return Array.from({ length: Math.floor(BYTES_TO_ALLOCATE_FOR_OUT_PARAMS / size) }, (_, i) => i).join(' | ');
}
const typesDocument = `// THIS FILE IS AUTOMATICALLY GENERATED BY ${path.basename(__filename)}
// DO NOT EDIT IT BY HAND
interface Pointer<T extends string> extends Number {
readonly __typeName: T;
}
interface Subpointer<T extends string, S extends string> extends Pointer<S> {
readonly __typeName2: T;
}
${Object.keys(types)
.filter(k => k.startsWith('Z3'))
.map(makePointerType)
.join('\n')}
${Object.entries(enums)
.map(e => wrapEnum(e[0], e[1]))
.join('\n\n')}
`;
const relativePath: string = path.relative(path.dirname(wrapperFilePath), path.dirname(typesFilePath)) || './';
const ext: string = path.extname(typesFilePath);
const basename: string = path.basename(typesFilePath);
const importPath = relativePath + basename.slice(0, -ext.length);
const wrapperDocument = `// THIS FILE IS AUTOMATICALLY GENERATED BY ${path.basename(__filename)}
// DO NOT EDIT IT BY HAND
import {
${Object.keys(types)
.filter(k => k.startsWith('Z3'))
.join(',\n')},
${Object.keys(enums).join(',\n')},
} from '${importPath}';
${Object.entries(primitiveTypes)
.filter(e => e[0] !== 'void')
.map(e => `type ${e[0]} = ${e[1]};`)
.join('\n')}
export async function init(initModule: any) {
let Mod = await initModule();
// this works for both signed and unsigned, because JS will wrap for you when constructing the Uint32Array
function intArrayToByteArr(ints: number[]) {
return new Uint8Array((new Uint32Array(ints)).buffer);
}
function boolArrayToByteArr(bools: boolean[]) {
return bools.map(b => b ? 1 : 0);
}
function readUintArray(address: number, count: number) {
return Array.from(new Uint32Array(Mod.HEAPU32.buffer, address, count));
}
let outAddress = Mod._malloc(${BYTES_TO_ALLOCATE_FOR_OUT_PARAMS});
let outUintArray = (new Uint32Array(Mod.HEAPU32.buffer, outAddress, 4));
let getOutUint = (i: ${getValidOutArrayIndexes(4)}) => outUintArray[i];
let outIntArray = (new Int32Array(Mod.HEAPU32.buffer, outAddress, 4));
let getOutInt = (i: ${getValidOutArrayIndexes(4)}) => outIntArray[i];
let outUint64Array = (new BigUint64Array(Mod.HEAPU32.buffer, outAddress, 2));
let getOutUint64 = (i: ${getValidOutArrayIndexes(8)}) => outUint64Array[i];
let outInt64Array = (new BigInt64Array(Mod.HEAPU32.buffer, outAddress, 2));
let getOutInt64 = (i: ${getValidOutArrayIndexes(8)}) => outInt64Array[i];
return {
em: Mod,
Z3: {
mk_context: function(c: Z3_config): Z3_context {
let ctx = Mod._Z3_mk_context(c);
Mod._set_noop_error_handler(ctx);
return ctx;
},
mk_context_rc: function(c: Z3_config): Z3_context {
let ctx = Mod._Z3_mk_context_rc(c);
Mod._set_noop_error_handler(ctx);
return ctx;
},
${functions
.map(wrapFunction)
.filter(f => f != null)
.join(',\n')}
}
};
}
`;
return {
wrapperDocument: prettier.format(wrapperDocument, { singleQuote: true, parser: 'typescript' }),
typesDocument: prettier.format(typesDocument, { singleQuote: true, parser: 'typescript' }),
};
}
const { wrapperDocument, typesDocument } = makeTsWrapper();
fs.mkdirSync(path.dirname(wrapperFilePath), { recursive: true });
fs.writeFileSync(wrapperFilePath, wrapperDocument);
fs.mkdirSync(path.dirname(typesFilePath), { recursive: true });
fs.writeFileSync(typesFilePath, typesDocument);

View file

@ -1,9 +1,8 @@
'use strict';
import assert from 'assert';
import fs from 'fs';
import path from 'path';
let fs = require('fs');
let path = require('path');
let files = [
const files = [
'z3_api.h',
'z3_algebraic.h',
'z3_ast_containers.h',
@ -15,15 +14,15 @@ let files = [
'z3_spacer.h',
];
let aliases = {
const aliases = {
__proto__: null,
Z3_bool: 'boolean',
Z3_string: 'string',
bool: 'boolean',
signed: 'int',
};
} as unknown as Record<string, string>;
let primitiveTypes = {
const primitiveTypes = {
__proto__: null,
Z3_char_ptr: 'string',
unsigned: 'number',
@ -32,18 +31,18 @@ let primitiveTypes = {
int64_t: 'bigint',
double: 'number',
float: 'number',
};
} as unknown as Record<string, string>;
let optTypes = {
const optTypes = {
__proto__: null,
Z3_sort_opt: 'Z3_sort',
Z3_ast_opt: 'Z3_ast',
Z3_func_interp_opt: 'Z3_func_interp',
};
} as unknown as Record<string, string>;
// parse type declarations
let types = {
const types = {
__proto__: null,
// these are function types I can't be bothered to parse
@ -55,11 +54,26 @@ let types = {
Z3_eq_eh: 'Z3_eq_eh',
Z3_final_eh: 'Z3_final_eh',
Z3_created_eh: 'Z3_created_eh',
};
Z3_decide_eh: 'Z3_decide_eh',
} as unknown as Record<string, string>;
let defApis = Object.create(null);
let functions = [];
let enums = Object.create(null);
export type ApiParam = { kind: string; sizeIndex?: number; type: string };
export type Api = { params: ApiParam[]; ret: string; extra: boolean };
const defApis: Record<string, Api> = Object.create(null);
export type FuncParam = {
type: string;
cType: string;
name: string;
isConst: boolean;
isPtr: boolean;
isArray: boolean;
nullable: boolean;
kind?: string;
sizeIndex?: number;
};
export type Func = { ret: string; cRet: string; name: string; params: FuncParam[]; nullableRet: boolean };
const functions: Func[] = [];
let enums: Record<string, Record<string, number>> = Object.create(null);
for (let file of files) {
let contents = fs.readFileSync(path.join(__dirname, '..', '..', file), 'utf8');
@ -79,31 +93,40 @@ for (let file of files) {
/def_Type\(\s*'(?<name>[A-Za-z0-9_]+)',\s*'(?<cname>[A-Za-z0-9_]+)',\s*'(?<pname>[A-Za-z0-9_]+)'\)/g,
);
for (let { groups } of typeMatches) {
assert(groups !== undefined);
pytypes[groups.name] = groups.cname;
}
// we don't have to pre-populate the types map with closure types
// use the Z3_DECLARE_CLOSURE to identify closure types
// for (let match of contents.matchAll(/Z3_DECLARE_CLOSURE\((?<type>[A-Za-z0-9_]+),/g)) {
// types[match.groups.type] = match.groups.type
// }
// we filter first to ensure our regex isn't too strict
let apiLines = contents.split('\n').filter(l => /def_API|extra_API/.test(l));
for (let line of apiLines) {
let match = line.match(
/^\s*(?<def>def_API|extra_API) *\(\s*'(?<name>[A-Za-z0-9_]+)'\s*,\s*(?<ret>[A-Za-z0-9_]+)\s*,\s*\((?<params>((_in|_out|_in_array|_out_array|_inout_array)\([^)]+\)\s*,?\s*)*)\)\s*\)\s*$/,
/^\s*(?<def>def_API|extra_API) *\(\s*'(?<name>[A-Za-z0-9_]+)'\s*,\s*(?<ret>[A-Za-z0-9_]+)\s*,\s*\((?<params>((_in|_out|_in_array|_out_array|_fnptr|_inout_array)\([^)]+\)\s*,?\s*)*)\)\s*\)\s*$/,
);
if (match == null) {
if (match === null) {
throw new Error(`failed to match def_API call ${JSON.stringify(line)}`);
}
assert(match.groups !== undefined);
let { name, ret, def } = match.groups;
let params = match.groups.params.trim();
let text = params;
let parsedParams = [];
while (true) {
text = eatWs(text);
({ text, match } = eat(text, /^_(?<kind>in|out|in_array|out_array|inout_array)\(/));
({ text, match } = eat(text, /^_(?<kind>in|out|in_array|out_array|inout_array|fnptr)\(/));
if (match == null) {
break;
}
assert(match.groups !== undefined);
let kind = match.groups.kind;
if (kind === 'inout_array') kind = 'in_array'; // https://github.com/Z3Prover/z3/discussions/5761
if (kind === 'in' || kind === 'out') {
if (kind === 'in' || kind === 'out' || kind == 'fnptr') {
({ text, match } = expect(text, /^[A-Za-z0-9_]+/));
parsedParams.push({ kind, type: match[0] });
} else {
@ -121,7 +144,6 @@ for (let file of files) {
throw new Error(`extra text in parameter list ${JSON.stringify(text)}`);
}
if (name in defApis) {
throw new Error(`multiple defApi calls for ${name}`);
}
@ -129,15 +151,10 @@ for (let file of files) {
}
for (let match of contents.matchAll(/DEFINE_TYPE\((?<type>[A-Za-z0-9_]+)\)/g)) {
assert(match.groups !== undefined);
types[match.groups.type] = match.groups.type;
}
// we don't have to pre-populate the types map with closure types
// use the Z3_DECLARE_CLOSURE to identify closure types
// for (let match of contents.matchAll(/Z3_DECLARE_CLOSURE\((?<type>[A-Za-z0-9_]+),/g)) {
// types[match.groups.type] = match.groups.type
// }
// parse enum declarations
for (let idx = 0; idx < contents.length; ) {
let nextIdx = contents.indexOf('typedef enum', idx);
@ -155,12 +172,13 @@ for (let file of files) {
if (match === null) {
throw new Error(`could not parse enum ${JSON.stringify(slice)}`);
}
let vals = Object.create(null);
let vals: Record<string, number> = Object.create(null);
let next = 0;
while (true) {
let blank = true;
while (blank) {
({ match, text } = eat(text, /^\s*(\/\/[^\n]*\n)?/));
assert(match !== null);
blank = match[0].length > 0;
}
({ match, text } = eat(text, /^[A-Za-z0-9_]+/));
@ -172,6 +190,7 @@ for (let file of files) {
({ match, text } = eat(text, /^= *(?<val>[^\n,\s]+)/));
if (match !== null) {
assert(match.groups !== undefined);
let parsedVal = Number(match.groups.val);
if (Object.is(parsedVal, NaN)) {
throw new Error('unknown value ' + match.groups.val);
@ -221,12 +240,14 @@ for (let file of files) {
if (match == null) {
throw new Error(`failed to match c definition: ${JSON.stringify(slice)}`);
}
assert(match.groups !== undefined);
let { ret, name, params } = match.groups;
let parsedParams = [];
if (params.trim() !== 'void') {
for (let param of params.split(',')) {
let paramType, paramName, isConst, isPtr, isArray;
let paramType: string, paramName: string, isConst: boolean, isPtr: boolean, isArray: boolean;
let { match, text } = eat(param, /^\s*/);
({ match, text } = eat(text, /^[A-Za-z0-9_]+/));
@ -302,7 +323,7 @@ for (let file of files) {
}
}
function isKnownType(t) {
function isKnownType(t: string) {
return t in enums || t in types || t in primitiveTypes || ['string', 'boolean', 'void'].includes(t);
}
@ -339,19 +360,19 @@ for (let fn of functions) {
}
}
function eat(str, regex) {
const match = str.match(regex);
if (match == null) {
function eat(str: string, regex: string | RegExp) {
const match: RegExpMatchArray | null = str.match(regex);
if (match === null) {
return { match, text: str };
}
return { match, text: str.substring(match[0].length) };
}
function eatWs(text) {
function eatWs(text: string) {
return eat(text, /^\s*/).text;
}
function expect(str, regex) {
function expect(str: string, regex: string | RegExp) {
let { text, match } = eat(str, regex);
if (match === null) {
throw new Error(`expected ${regex}, got ${JSON.stringify(text)}`);
@ -359,4 +380,4 @@ function expect(str, regex) {
return { text, match };
}
module.exports = { primitiveTypes, types, enums, functions };
export { primitiveTypes, types, enums, functions };

16
src/api/js/src/browser.ts Normal file
View file

@ -0,0 +1,16 @@
import { createApi, Z3HighLevel } from './high-level';
import { init as initWrapper, Z3LowLevel } from './low-level';
export * from './high-level/types';
export { Z3Core, Z3LowLevel } from './low-level';
export * from './low-level/types.__GENERATED__';
export async function init(): Promise<Z3LowLevel & Z3HighLevel> {
const initZ3 = (global as any).initZ3;
if (initZ3 === undefined) {
throw new Error('initZ3 was not imported correctly. Please consult documentation on how to load Z3 in browser');
}
const lowLevel = await initWrapper(initZ3);
const highLevel = createApi(lowLevel.Z3);
return { ...lowLevel, ...highLevel };
}

View file

@ -0,0 +1,450 @@
import assert from 'assert';
import asyncToArray from 'iter-tools/methods/async-to-array';
import { init, killThreads } from '../jest';
import { Arith, Bool, Model, Z3AssertionError, Z3HighLevel } from './types';
/**
* Generate all possible solutions from given assumptions.
*
* **NOTE**: The set of solutions might be infinite.
* Always ensure to limit amount generated, either by knowing that the
* solution space is constrainted, or by taking only a specified
* amount of solutions
* ```typescript
* import { sliceAsync } from 'iter-tools';
* // ...
* for await (const model of sliceAsync(10, solver.solutions())) {
* console.log(model.sexpr());
* }
* ```
* @see http://theory.stanford.edu/~nikolaj/programmingz3.html#sec-blocking-evaluations
* @returns Models with solutions. Nothing if no constants provided
*/
// TODO(ritave): Use faster solution https://stackoverflow.com/a/70656700
// TODO(ritave): Move to high-level.ts
async function* allSolutions<Name extends string>(...assertions: Bool<Name>[]): AsyncIterable<Model<Name>> {
if (assertions.length === 0) {
return;
}
const { Or } = assertions[0].ctx;
const solver = new assertions[0].ctx.Solver();
solver.add(...assertions);
while ((await solver.check()) === 'sat') {
const model = solver.model();
const decls = model.decls();
if (decls.length === 0) {
return;
}
yield model;
solver.add(
Or(
...decls
// TODO(ritave): Assert on arity > 0
.filter(decl => decl.arity() === 0)
.map(decl => {
const term = decl.call();
// TODO(ritave): Assert not an array / uinterpeted sort
const value = model.eval(term, true);
return term.neq(value);
}),
),
);
}
}
async function prove(conjecture: Bool): Promise<void> {
const solver = new conjecture.ctx.Solver();
const { Not } = solver.ctx;
solver.add(Not(conjecture));
expect(await solver.check()).toStrictEqual('unsat');
}
async function solve(conjecture: Bool): Promise<Model> {
const solver = new conjecture.ctx.Solver();
solver.add(conjecture);
expect(await solver.check()).toStrictEqual('sat');
return solver.model();
}
describe('high-level', () => {
let api: { em: any } & Z3HighLevel;
beforeAll(async () => {
api = await init();
});
afterAll(async () => {
await killThreads(api.em);
});
it('can set params', () => {
const { setParam, getParam, resetParams } = api;
expect(getParam('pp.decimal')).toStrictEqual('false');
setParam('pp.decimal', 'true');
expect(getParam('pp.decimal')).toStrictEqual('true');
setParam({ 'pp.decimal': 'false', timeout: 4 });
expect(getParam('pp.decimal')).toStrictEqual('false');
expect(getParam('timeout')).toStrictEqual('4');
resetParams();
expect(getParam('pp.decimal')).toStrictEqual('false');
expect(getParam('timeout')).toStrictEqual('4294967295');
});
it('proves x = y implies g(x) = g(y)', async () => {
const { Solver, Int, Function, Implies, Not } = api.Context('main');
const solver = new Solver();
const sort = Int.sort();
const x = Int.const('x');
const y = Int.const('y');
const g = Function.declare('g', sort, sort);
const conjecture = Implies(x.eq(y), g.call(x).eq(g.call(y)));
solver.add(Not(conjecture));
expect(await solver.check()).toStrictEqual('unsat');
});
it('disproves x = y implies g(g(x)) = g(y)', async () => {
const { Solver, Int, Function, Implies, Not } = api.Context('main');
const solver = new Solver();
const sort = Int.sort();
const x = Int.const('x');
const y = Int.const('y');
const g = Function.declare('g', sort, sort);
const conjecture = Implies(x.eq(y), g.call(g.call(x)).eq(g.call(y)));
solver.add(Not(conjecture));
expect(await solver.check()).toStrictEqual('sat');
});
it('checks that Context matches', () => {
const c1 = api.Context('context');
const c2 = api.Context('context');
const c3 = api.Context('foo');
const c4 = api.Context('bar');
// Contexts with the same name don't do type checking during compile time.
// We need to check for different context dynamically
expect(() => c1.Or(c2.Int.val(5).eq(2))).toThrowError(Z3AssertionError);
// On the other hand, this won't compile due to automatic generics
// @ts-expect-error
expect(() => c3.Or(c4.Int.val(5).eq(2))).toThrowError(Z3AssertionError);
const allUniqueContexes = new Set([c1, c2, c3, c4]).size === 4;
expect(allUniqueContexes).toStrictEqual(true);
expect(() => c1.Or(c1.Int.val(5).eq(2))).not.toThrowError();
});
describe('booleans', () => {
it("proves De Morgan's Law", async () => {
const { Bool, Not, And, Eq, Or } = api.Context('main');
const [x, y] = [Bool.const('x'), Bool.const('y')];
const conjecture = Eq(Not(And(x, y)), Or(Not(x), Not(y)));
await prove(conjecture);
});
});
describe('ints', () => {
it('finds a model', async () => {
const { Solver, Int, isIntVal } = api.Context('main');
const solver = new Solver();
const x = Int.const('x');
const y = Int.const('y');
solver.add(x.ge(1)); // x >= 1
solver.add(y.lt(x.add(3))); // y < x + 3
expect(await solver.check()).toStrictEqual('sat');
const model = solver.model();
expect(model.length()).toStrictEqual(2);
for (const decl of model) {
expect(decl.arity()).toStrictEqual(0);
}
const xValueExpr = model.get(x);
const yValueExpr = model.get(y);
assert(isIntVal(xValueExpr));
assert(isIntVal(yValueExpr));
const xValue = xValueExpr.value();
const yValue = yValueExpr.value();
assert(typeof xValue === 'bigint');
assert(typeof yValue === 'bigint');
expect(xValue).toBeGreaterThanOrEqual(1n);
expect(yValue).toBeLessThanOrEqual(xValue + 3n);
});
// TODO(ritave): After changes made since last commit (a332187c746c23450860deb210d94e6e042dd424),
// this test takes twice as long (from 5s to 10s). Figure out why
it('solves sudoku', async () => {
function toSudoku(data: string): (number | null)[][] {
const cells: (number | null)[][] = Array.from({ length: 9 }, () => Array.from({ length: 9 }, () => null));
const lines = data.trim().split('\n');
for (let row = 0; row < 9; row++) {
const line = lines[row].trim();
for (let col = 0; col < 9; col++) {
const char = line[col];
if (char !== '.') {
cells[row][col] = Number.parseInt(char);
}
}
}
return cells;
}
const INSTANCE = toSudoku(`
....94.3.
...51...7
.89....4.
......2.8
.6.2.1.5.
1.2......
.7....52.
9...65...
.4.97....
`);
const EXPECTED = toSudoku(`
715894632
234516897
689723145
493657218
867231954
152489763
376148529
928365471
541972386
`);
const { Solver, Int, Distinct, isIntVal } = api.Context('main');
const cells: Arith[][] = [];
// 9x9 matrix of integer variables
for (let i = 0; i < 9; i++) {
const row = [];
for (let j = 0; j < 9; j++) {
row.push(Int.const(`x_${i}_${j}`));
}
cells.push(row);
}
const solver = new Solver();
// each cell contains a value 1<=x<=9
for (let i = 0; i < 9; i++) {
for (let j = 0; j < 9; j++) {
solver.add(cells[i][j].ge(1), cells[i][j].le(9));
}
}
// each row contains a digit only once
for (let i = 0; i < 9; i++) {
solver.add(Distinct(...cells[i]));
}
// each column contains a digit only once
for (let j = 0; j < 9; j++) {
const column = [];
for (let i = 0; i < 9; i++) {
column.push(cells[i][j]);
}
solver.add(Distinct(...column));
}
// each 3x3 contains a digit at most once
for (let iSquare = 0; iSquare < 3; iSquare++) {
for (let jSquare = 0; jSquare < 3; jSquare++) {
const square = [];
for (let i = iSquare * 3; i < iSquare * 3 + 3; i++) {
for (let j = jSquare * 3; j < jSquare * 3 + 3; j++) {
square.push(cells[i][j]);
}
}
solver.add(Distinct(...square));
}
}
for (let i = 0; i < 9; i++) {
for (let j = 0; j < 9; j++) {
const digit = INSTANCE[i][j];
if (digit !== null) {
solver.add(cells[i][j].eq(digit));
}
}
}
expect(await solver.check()).toStrictEqual('sat');
const model = solver.model();
const result = [];
for (let i = 0; i < 9; i++) {
let row = [];
for (let j = 0; j < 9; j++) {
const cell = model.eval(cells[i][j]);
assert(isIntVal(cell));
const value = cell.value();
assert(typeof value === 'bigint');
expect(value).toBeGreaterThanOrEqual(0n);
expect(value).toBeLessThanOrEqual(9n);
// JSON.stringify doesn't handle bigints
row.push(Number(value));
}
result.push(row);
}
expect(JSON.stringify(result)).toStrictEqual(JSON.stringify(EXPECTED));
}, 120_000);
});
describe('reals', () => {
it('can work with numerals', async () => {
const { Real, And } = api.Context('main');
const n1 = Real.val('1/2');
const n2 = Real.val('0.5');
const n3 = Real.val(0.5);
await prove(And(n1.eq(n2), n1.eq(n3)));
const n4 = Real.val('-1/3');
const n5 = Real.val('-0.3333333333333333333333333333333333');
await prove(n4.neq(n5));
});
it('can do non-linear arithmetic', async () => {
api.setParam('pp.decimal', true);
api.setParam('pp.decimal_precision', 20);
const { Real, Solver, isReal, isRealVal } = api.Context('main');
const x = Real.const('x');
const y = Real.const('y');
const z = Real.const('z');
const solver = new Solver();
solver.add(x.mul(x).add(y.mul(y)).eq(1)); // x^2 + y^2 == 1
solver.add(x.mul(x).mul(x).add(z.mul(z).mul(z)).lt('1/2')); // x^3 + z^3 < 1/2
expect(await solver.check()).toStrictEqual('sat');
const model = solver.model();
expect(isRealVal(model.get(x))).toStrictEqual(true);
// solution of y is a polynomial
// https://stackoverflow.com/a/69740906
expect(isReal(model.get(y))).toStrictEqual(true);
expect(isRealVal(model.get(z))).toStrictEqual(true);
});
});
describe('bitvectors', () => {
it('can do simple proofs', async () => {
const { BitVec, Concat, Implies, isBitVecVal } = api.Context('main');
const x = BitVec.const('x', 32);
const sSol = (await solve(x.sub(10).sle(0).eq(x.sle(10)))).get(x); // signed: (x - 10 <= 0) == (x <= 10)
const uSol = (await solve(x.sub(10).ule(0).eq(x.ule(10)))).get(x); // unsigned: (x - 10 <= 0) == (x <= 10)
assert(isBitVecVal(sSol) && isBitVecVal(uSol));
let v = sSol.asSignedValue();
expect(v - 10n <= 0n === v <= 10n).toStrictEqual(true);
v = uSol.value();
expect(v - 10n <= 0n === v <= 10n).toStrictEqual(true);
const y = BitVec.const('y', 32);
await prove(Implies(Concat(x, y).eq(Concat(y, x)), x.eq(y)));
});
it('finds x and y such that: x ^ y - 103 == x * y', async () => {
const { BitVec, isBitVecVal } = api.Context('main');
const x = BitVec.const('x', 32);
const y = BitVec.const('y', 32);
const model = await solve(x.xor(y).sub(103).eq(x.mul(y)));
const xSol = model.get(x);
const ySol = model.get(y);
assert(isBitVecVal(xSol) && isBitVecVal(ySol));
const xv = xSol.asSignedValue();
const yv = ySol.asSignedValue();
// this solutions wraps around so we need to check using modulo
expect((xv ^ yv) - 103n === (xv * yv) % 2n ** 32n).toStrictEqual(true);
});
});
describe('Solver', () => {
it('can use push and pop', async () => {
const { Solver, Int } = api.Context('main');
const solver = new Solver();
const x = Int.const('x');
solver.add(x.gt(0));
expect(await solver.check()).toStrictEqual('sat');
solver.push();
solver.add(x.lt(0));
expect(solver.numScopes()).toStrictEqual(1);
expect(await solver.check()).toStrictEqual('unsat');
solver.pop();
expect(solver.numScopes()).toStrictEqual(0);
expect(await solver.check()).toStrictEqual('sat');
});
it('can find multiple solutions', async () => {
const { Int, isIntVal } = api.Context('main');
const x = Int.const('x');
const solutions = await asyncToArray(allSolutions(x.ge(1), x.le(5)));
expect(solutions.length).toStrictEqual(5);
const results = solutions
.map(solution => {
const expr = solution.eval(x);
assert(isIntVal(expr));
return expr.value();
})
.sort((a, b) => {
assert(a !== null && b !== null && typeof a === 'bigint' && typeof b === 'bigint');
if (a < b) {
return -1;
} else if (a == b) {
return 0;
} else {
return 1;
}
});
expect(results).toStrictEqual([1n, 2n, 3n, 4n, 5n]);
});
});
describe('AstVector', () => {
it('can use basic methods', async () => {
const { Solver, AstVector, Int } = api.Context('main');
const solver = new Solver();
const vector = new AstVector<Arith>();
for (let i = 0; i < 5; i++) {
vector.push(Int.const(`int__${i}`));
}
const length = vector.length();
for (let i = 0; i < length; i++) {
solver.add(vector.get(i).gt(1));
}
expect(await solver.check()).toStrictEqual('sat');
});
});
});

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,2 @@
export * from './high-level';
export * from './types';

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,68 @@
import { Z3AssertionError } from './types';
import { allSatisfy, assert, assertExhaustive } from './utils';
describe('allSatisfy', () => {
it('returns null on empty array', () => {
expect(allSatisfy([], () => true)).toBeNull();
});
it('returns true if all satisfy', () => {
expect(allSatisfy([2, 4, 6, 8], arg => arg % 2 === 0)).toStrictEqual(true);
});
it('returns false if any element fails', () => {
expect(allSatisfy([2, 4, 1, 8], arg => arg % 2 === 0)).toStrictEqual(false);
});
});
describe('assertExhaustive', () => {
enum MyEnum {
A,
B,
}
it('stops compilation', () => {
const result: MyEnum = MyEnum.A as any;
switch (result) {
case MyEnum.A:
break;
default:
// @ts-expect-error
assertExhaustive(result);
}
});
it('allows compilation', () => {
const result: MyEnum = MyEnum.A as any;
switch (result) {
case MyEnum.A:
break;
case MyEnum.B:
break;
default:
assertExhaustive(result);
}
});
it('throws', () => {
const result: MyEnum = undefined as any;
switch (result) {
case MyEnum.A:
throw new Error();
case MyEnum.B:
throw new Error();
default:
expect(() => assertExhaustive(result)).toThrowError();
}
});
});
describe('assert', () => {
it('throws on failure', () => {
expect(() => assert(false)).toThrowError(Z3AssertionError);
expect(() => assert(false, 'foobar')).toThrowError(new Z3AssertionError('foobar'));
});
it('does nothing on sucess', () => {
expect(() => assert(true, 'hello')).not.toThrow();
});
});

View file

@ -0,0 +1,59 @@
import { Z3AssertionError } from './types';
function getAllProperties(obj: Record<string, any>) {
const properties = new Set<[any, string | symbol]>();
do {
for (const key of Reflect.ownKeys(obj)) {
properties.add([obj, key]);
}
} while ((obj = Reflect.getPrototypeOf(obj)!) && obj !== Object.prototype);
return properties;
}
/**
* Use to ensure that switches are checked to be exhaustive at compile time
*
* @example Basic usage
* ```typescript
* enum Something {
* left,
* right,
* };
* const something = getSomething();
* switch (something) {
* case Something.left:
* ...
* case Something.right:
* ...
* default:
* assertExhaustive(something);
* }
* ```
*
* @param x - The param on which the switch operates
*/
export function assertExhaustive(x: never): never {
throw new Error('Unexpected code execution detected, should be caught at compile time');
}
export function assert(condition: boolean, reason?: string): asserts condition {
if (!condition) {
throw new Z3AssertionError(reason ?? 'Assertion failed');
}
}
/**
* Check the all elements of a `collection` satisfy the `premise`.
* If any of the items fail the `premise`, returns false;
* @returns null if the `collection` is empty, boolean otherwise
*/
export function allSatisfy<T>(collection: Iterable<T>, premise: (arg: T) => boolean): boolean | null {
let hasItems = false;
for (const arg of collection) {
hasItems = true;
if (!premise(arg)) {
return false;
}
}
return hasItems === true ? true : null;
}

62
src/api/js/src/jest.ts Normal file
View file

@ -0,0 +1,62 @@
// This file is not included in the build
// @ts-ignore no-implicit-any
import { createApi, Z3HighLevel } from './high-level';
import { init as initWrapper, Z3LowLevel } from './low-level';
import initModule = require('../build/z3-built');
export * from './high-level/types';
export { Z3Core, Z3LowLevel } from './low-level';
export * from './low-level/types.__GENERATED__';
export async function init(): Promise<Z3HighLevel & Z3LowLevel> {
const lowLevel = await initWrapper(initModule);
const highLevel = createApi(lowLevel.Z3);
return { ...lowLevel, ...highLevel };
}
function delay(ms: number): Promise<void> & { cancel(): void };
function delay(ms: number, result: Error): Promise<never> & { cancel(): void };
function delay<T>(ms: number, result: T): Promise<T> & { cancel(): void };
function delay<T>(ms: number, result?: T | Error): Promise<T | void> & { cancel(): void } {
let handle: any;
const promise = new Promise<void | T>(
(resolve, reject) =>
(handle = setTimeout(() => {
if (result instanceof Error) {
reject(result);
} else if (result !== undefined) {
resolve(result);
}
resolve();
}, ms)),
);
return { ...promise, cancel: () => clearTimeout(handle) };
}
function waitWhile(premise: () => boolean, pollMs: number = 100): Promise<void> & { cancel(): void } {
let handle: any;
const promise = new Promise<void>(resolve => {
handle = setInterval(() => {
if (premise()) {
clearTimeout(handle);
resolve();
}
}, pollMs);
});
return { ...promise, cancel: () => clearInterval(handle) };
}
export function killThreads(em: any): Promise<void> {
em.PThread.terminateAllThreads();
// Create a polling lock to wait for threads to return
// TODO(ritave): Threads should be killed automatically, or there should be a better way to wait for them
const lockPromise = waitWhile(() => !em.PThread.unusedWorkers.length && !em.PThread.runningWorkers.length);
const delayPromise = delay(5000, new Error('Waiting for threads to be killed timed out'));
return Promise.race([lockPromise, delayPromise]).finally(() => {
lockPromise.cancel();
delayPromise.cancel();
});
}

View file

@ -1,4 +1,5 @@
// this wrapper works with async-fns to provide promise-based off-thread versions of some functions
// It's prepended directly by emscripten to the resulting z3-built.js
let capability = null;
function resolve_async(val) {

View file

@ -0,0 +1,4 @@
export * from './types.__GENERATED__';
export * from './wrapper.__GENERATED__';
export type Z3Core = Awaited<ReturnType<typeof import('./wrapper.__GENERATED__')['init']>>['Z3'];
export type Z3LowLevel = Awaited<ReturnType<typeof import('./wrapper.__GENERATED__')['init']>>;

38
src/api/js/src/node.ts Normal file
View file

@ -0,0 +1,38 @@
// @ts-ignore no-implicit-any
import initModule = require('./z3-built');
import { createApi, Z3HighLevel } from './high-level';
import { init as initWrapper, Z3LowLevel } from './low-level';
export * from './high-level/types';
export { Z3Core, Z3LowLevel } from './low-level';
export * from './low-level/types.__GENERATED__';
/**
* The main entry point to the Z3 API
*
* ```typescript
* import { init, sat } from 'z3-solver';
*
* const { Context } = await init();
* const { Solver, Int } = new Context('main');
*
* const x = Int.const('x');
* const y = Int.const('y');
*
* const solver = new Solver();
* solver.add(x.add(2).le(y.sub(10))); // x + 2 <= y - 10
*
* if (await solver.check() !== sat) {
* throw new Error("couldn't find a solution")
* }
* const model = solver.model();
*
* console.log(`x=${model.get(x)}, y=${model.get(y)}`);
* // x=0, y=12
* ```
* @category Global */
export async function init(): Promise<Z3HighLevel & Z3LowLevel> {
const lowLevel = await initWrapper(initModule);
const highLevel = createApi(lowLevel.Z3);
return { ...lowLevel, ...highLevel };
}

View file

@ -0,0 +1,4 @@
{
"extends": "./tsconfig.json",
"exclude": ["src/**/*.test.ts", "src/jest.ts"]
}

View file

@ -6,9 +6,11 @@
"declaration": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
"skipLibCheck": true,
"outDir": "build/",
"allowJs": true,
"checkJs": false
},
"exclude": [
"src"
]
"include": ["src/**/*.ts"],
"exclude": ["node_modules"]
}

8
src/api/js/typedoc.json Normal file
View file

@ -0,0 +1,8 @@
{
"$schema": "https://typedoc.org/schema.json",
"entryPoints": ["./src/node.ts"],
"out": "../../../doc/api/html/js",
"exclude": ["./src/low-level/**/*"],
"readme": "./PUBLISHED_README.md",
"categorizeByGroup": false
}

View file

@ -119,6 +119,7 @@ sig
val get_ast_kind : ast -> Z3enums.ast_kind
val is_expr : ast -> bool
val is_app : ast -> bool
val is_numeral : ast -> bool
val is_var : ast -> bool
val is_quantifier : ast -> bool
val is_sort : ast -> bool
@ -191,6 +192,7 @@ end = struct
| _ -> false
let is_app (x:ast) = get_ast_kind x = APP_AST
let is_numeral (x:ast) = get_ast_kind x = NUMERAL_AST
let is_var (x:ast) = get_ast_kind x = VAR_AST
let is_quantifier (x:ast) = get_ast_kind x = QUANTIFIER_AST
let is_sort (x:ast) = get_ast_kind x = SORT_AST
@ -914,6 +916,12 @@ struct
let mk_sort_s (ctx:context) (name:string) (constructors:Constructor.constructor list) =
mk_sort ctx (Symbol.mk_string ctx name) constructors
let mk_sort_ref (ctx: context) (name:Symbol.symbol) =
Z3native.mk_datatype_sort ctx name
let mk_sort_ref_s (ctx: context) (name: string) =
mk_sort_ref ctx (Symbol.mk_string ctx name)
let mk_sorts (ctx:context) (names:Symbol.symbol list) (c:Constructor.constructor list list) =
let n = List.length names in
@ -1018,7 +1026,7 @@ struct
let is_int (x:expr) =
((sort_kind_of_int (Z3native.get_sort_kind (Expr.gc x) (Z3native.get_sort (Expr.gc x) x))) = INT_SORT)
let is_arithmetic_numeral (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_ANUM)
let is_arithmetic_numeral (x:expr) = (AST.is_numeral x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_ANUM)
let is_le (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_LE)
let is_ge (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_GE)
let is_lt (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_LT)
@ -1129,7 +1137,7 @@ struct
let mk_sort (ctx:context) size = Z3native.mk_bv_sort ctx size
let is_bv (x:expr) =
((sort_kind_of_int (Z3native.get_sort_kind (Expr.gc x) (Z3native.get_sort (Expr.gc x) x))) = BV_SORT)
let is_bv_numeral (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_BNUM)
let is_bv_numeral (x:expr) = (AST.is_numeral x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_BNUM)
let is_bv_bit1 (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_BIT1)
let is_bv_bit0 (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_BIT0)
let is_bv_uminus (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_BNEG)
@ -1258,7 +1266,9 @@ struct
let mk_seq_replace = Z3native.mk_seq_replace
let mk_seq_at = Z3native.mk_seq_at
let mk_seq_length = Z3native.mk_seq_length
let mk_seq_nth = Z3native.mk_seq_nth
let mk_seq_index = Z3native.mk_seq_index
let mk_seq_last_index = Z3native.mk_seq_last_index
let mk_str_to_int = Z3native.mk_str_to_int
let mk_str_le = Z3native.mk_str_le
let mk_str_lt = Z3native.mk_str_lt

View file

@ -1061,6 +1061,15 @@ sig
if the corresponding sort reference is 0, then the value in sort_refs should be an index
referring to one of the recursive datatypes that is declared. *)
val mk_constructor_s : context -> string -> Symbol.symbol -> Symbol.symbol list -> Sort.sort option list -> int list -> Constructor.constructor
(* Create a forward reference to a recursive datatype being declared.
The forward reference can be used in a nested occurrence: the range of an array
or as element sort of a sequence. The forward reference should only be used when
used in an accessor for a recursive datatype that gets declared. *)
val mk_sort_ref : context -> Symbol.symbol -> Sort.sort
(* [mk_sort_ref_s ctx s] is [mk_sort_ref ctx (Symbol.mk_string ctx s)] *)
val mk_sort_ref_s : context -> string -> Sort.sort
(** Create a new datatype sort. *)
val mk_sort : context -> Symbol.symbol -> Constructor.constructor list -> Sort.sort
@ -1858,7 +1867,7 @@ sig
(** create regular expression sorts over sequences of the argument sort *)
val mk_re_sort : context -> Sort.sort -> Sort.sort
(** test if sort is a regular expression sort *)
val is_re_sort : context -> Sort.sort -> bool
@ -1906,10 +1915,17 @@ sig
(** length of a sequence *)
val mk_seq_length : context -> Expr.expr -> Expr.expr
(** [mk_seq_nth ctx s index] retrieves from [s] the element at position [index].
The function is under-specified if the index is out of bounds. *)
val mk_seq_nth : context -> Expr.expr -> Expr.expr -> Expr.expr
(** index of the first occurrence of the second argument in the first *)
val mk_seq_index : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr
(** [mk_seq_last_index ctx s substr] occurence of [substr] in the sequence [s] *)
val mk_seq_last_index : context -> Expr.expr -> Expr.expr -> Expr.expr
(** retrieve integer expression encoded in string *)
val mk_str_to_int : context -> Expr.expr -> Expr.expr

View file

@ -250,6 +250,16 @@ class clean(_clean):
#try: os.makedirs(os.path.join(ROOT_DIR, 'build'))
#except OSError: pass
# platform.freedesktop_os_release was added in 3.10
os_id = ''
if hasattr(platform, 'freedesktop_os_release'):
try:
osr = platform.freedesktop_os_release()
print(osr)
os_id = osr['ID']
except OSError:
pass
if 'bdist_wheel' in sys.argv and '--plat-name' not in sys.argv:
if RELEASE_DIR is None:
name = get_platform()
@ -271,13 +281,20 @@ if 'bdist_wheel' in sys.argv and '--plat-name' not in sys.argv:
# extract the architecture of the release from the directory name
arch = RELEASE_METADATA[1]
distos = RELEASE_METADATA[2]
if distos in ('debian', 'ubuntu') or 'linux' in distos:
raise Exception("Linux binary distributions must be built on centos to conform to PEP 513")
if distos in ('debian', 'ubuntu'):
raise Exception(
"Linux binary distributions must be built on centos to conform to PEP 513 or alpine if targetting musl"
)
elif distos == 'glibc':
if arch == 'x64':
plat_name = 'manylinux1_x86_64'
else:
plat_name = 'manylinux1_i686'
elif distos == 'linux' and os_id == 'alpine':
if arch == 'x64':
plat_name = 'musllinux_1_1_x86_64'
else:
plat_name = 'musllinux_1_1_i686'
elif distos == 'win':
if arch == 'x64':
plat_name = 'win_amd64'

View file

@ -209,7 +209,8 @@ class Context:
Z3_del_config(conf)
def __del__(self):
Z3_del_context(self.ctx)
if Z3_del_context is not None:
Z3_del_context(self.ctx)
self.ctx = None
self.eh = None
@ -225,6 +226,10 @@ class Context:
"""
Z3_interrupt(self.ref())
def param_descrs(self):
"""Return the global parameter description set."""
return ParamDescrsRef(Z3_get_global_param_descrs(self.ref()), self)
# Global Z3 context
_main_ctx = None
@ -342,7 +347,7 @@ class AstRef(Z3PPObject):
Z3_inc_ref(self.ctx.ref(), self.as_ast())
def __del__(self):
if self.ctx.ref() is not None and self.ast is not None:
if self.ctx.ref() is not None and self.ast is not None and Z3_dec_ref is not None:
Z3_dec_ref(self.ctx.ref(), self.as_ast())
self.ast = None
@ -1101,6 +1106,28 @@ class ExprRef(AstRef):
else:
return []
def from_string(self, s):
pass
def serialize(self):
s = Solver()
f = Function('F', self.sort(), BoolSort(self.ctx))
s.add(f(self))
return s.sexpr()
def deserialize(st):
"""inverse function to the serialize method on ExprRef.
It is made available to make it easier for users to serialize expressions back and forth between
strings. Solvers can be serialized using the 'sexpr()' method.
"""
s = Solver()
s.from_string(st)
if len(s.assertions()) != 1:
raise Z3Exception("single assertion expected")
fml = s.assertions()[0]
if fml.num_args() != 1:
raise Z3Exception("dummy function 'F' expected")
return fml.arg(0)
def _to_expr_ref(a, ctx):
if isinstance(a, Pattern):
@ -5102,7 +5129,7 @@ class ScopedConstructor:
self.ctx = ctx
def __del__(self):
if self.ctx.ref() is not None:
if self.ctx.ref() is not None and Z3_del_constructor is not None:
Z3_del_constructor(self.ctx.ref(), self.c)
@ -5114,7 +5141,7 @@ class ScopedConstructorList:
self.ctx = ctx
def __del__(self):
if self.ctx.ref() is not None:
if self.ctx.ref() is not None and Z3_del_constructor_list is not None:
Z3_del_constructor_list(self.ctx.ref(), self.c)
@ -5394,7 +5421,7 @@ class ParamsRef:
return ParamsRef(self.ctx, self.params)
def __del__(self):
if self.ctx.ref() is not None:
if self.ctx.ref() is not None and Z3_params_dec_ref is not None:
Z3_params_dec_ref(self.ctx.ref(), self.params)
def set(self, name, val):
@ -5459,7 +5486,7 @@ class ParamDescrsRef:
return ParamsDescrsRef(self.descr, self.ctx)
def __del__(self):
if self.ctx.ref() is not None:
if self.ctx.ref() is not None and Z3_param_descrs_dec_ref is not None:
Z3_param_descrs_dec_ref(self.ctx.ref(), self.descr)
def size(self):
@ -5522,7 +5549,7 @@ class Goal(Z3PPObject):
Z3_goal_inc_ref(self.ctx.ref(), self.goal)
def __del__(self):
if self.goal is not None and self.ctx.ref() is not None:
if self.goal is not None and self.ctx.ref() is not None and Z3_goal_dec_ref is not None:
Z3_goal_dec_ref(self.ctx.ref(), self.goal)
def depth(self):
@ -5826,7 +5853,7 @@ class AstVector(Z3PPObject):
Z3_ast_vector_inc_ref(self.ctx.ref(), self.vector)
def __del__(self):
if self.vector is not None and self.ctx.ref() is not None:
if self.vector is not None and self.ctx.ref() is not None and Z3_ast_vector_dec_ref is not None:
Z3_ast_vector_dec_ref(self.ctx.ref(), self.vector)
def __len__(self):
@ -5989,7 +6016,7 @@ class AstMap:
return AstMap(self.map, self.ctx)
def __del__(self):
if self.map is not None and self.ctx.ref() is not None:
if self.map is not None and self.ctx.ref() is not None and Z3_ast_map_dec_ref is not None:
Z3_ast_map_dec_ref(self.ctx.ref(), self.map)
def __len__(self):
@ -6108,7 +6135,7 @@ class FuncEntry:
return FuncEntry(self.entry, self.ctx)
def __del__(self):
if self.ctx.ref() is not None:
if self.ctx.ref() is not None and Z3_func_entry_dec_ref is not None:
Z3_func_entry_dec_ref(self.ctx.ref(), self.entry)
def num_args(self):
@ -6215,7 +6242,7 @@ class FuncInterp(Z3PPObject):
Z3_func_interp_inc_ref(self.ctx.ref(), self.f)
def __del__(self):
if self.f is not None and self.ctx.ref() is not None:
if self.f is not None and self.ctx.ref() is not None and Z3_func_interp_dec_ref is not None:
Z3_func_interp_dec_ref(self.ctx.ref(), self.f)
def else_value(self):
@ -6333,7 +6360,7 @@ class ModelRef(Z3PPObject):
Z3_model_inc_ref(self.ctx.ref(), self.model)
def __del__(self):
if self.ctx.ref() is not None:
if self.ctx.ref() is not None and Z3_model_dec_ref is not None:
Z3_model_dec_ref(self.ctx.ref(), self.model)
def __repr__(self):
@ -6443,7 +6470,25 @@ class ModelRef(Z3PPObject):
return None
r = _to_expr_ref(_r, self.ctx)
if is_as_array(r):
return self.get_interp(get_as_array_func(r))
fi = self.get_interp(get_as_array_func(r))
if fi is None:
return fi
e = fi.else_value()
if e is None:
return fi
if fi.arity() != 1:
return fi
srt = decl.range()
dom = srt.domain()
e = K(dom, e)
i = 0
sz = fi.num_entries()
n = fi.arity()
while i < sz:
fe = fi.entry(i)
e = Store(e, fe.arg_value(0), fe.value())
i += 1
return e
else:
return r
else:
@ -6662,7 +6707,7 @@ class Statistics:
return Statistics(self.stats, self.ctx)
def __del__(self):
if self.ctx.ref() is not None:
if self.ctx.ref() is not None and Z3_stats_dec_ref is not None:
Z3_stats_dec_ref(self.ctx.ref(), self.stats)
def __repr__(self):
@ -6855,7 +6900,7 @@ class Solver(Z3PPObject):
self.set("smtlib2_log", logFile)
def __del__(self):
if self.solver is not None and self.ctx.ref() is not None:
if self.solver is not None and self.ctx.ref() is not None and Z3_solver_dec_ref is not None:
Z3_solver_dec_ref(self.ctx.ref(), self.solver)
def set(self, *args, **keys):
@ -7378,7 +7423,7 @@ class Fixedpoint(Z3PPObject):
return FixedPoint(self.fixedpoint, self.ctx)
def __del__(self):
if self.fixedpoint is not None and self.ctx.ref() is not None:
if self.fixedpoint is not None and self.ctx.ref() is not None and Z3_fixedpoint_dec_ref is not None:
Z3_fixedpoint_dec_ref(self.ctx.ref(), self.fixedpoint)
def set(self, *args, **keys):
@ -7801,7 +7846,7 @@ class Optimize(Z3PPObject):
return Optimize(self.optimize, self.ctx)
def __del__(self):
if self.optimize is not None and self.ctx.ref() is not None:
if self.optimize is not None and self.ctx.ref() is not None and Z3_optimize_dec_ref is not None:
Z3_optimize_dec_ref(self.ctx.ref(), self.optimize)
if self._on_models_id is not None:
del _on_models[self._on_models_id]
@ -8026,7 +8071,7 @@ class ApplyResult(Z3PPObject):
return ApplyResult(self.result, self.ctx)
def __del__(self):
if self.ctx.ref() is not None:
if self.ctx.ref() is not None and Z3_apply_result_dec_ref is not None:
Z3_apply_result_dec_ref(self.ctx.ref(), self.result)
def __len__(self):
@ -8131,7 +8176,7 @@ class Tactic:
return Tactic(self.tactic, self.ctx)
def __del__(self):
if self.tactic is not None and self.ctx.ref() is not None:
if self.tactic is not None and self.ctx.ref() is not None and Z3_tactic_dec_ref is not None:
Z3_tactic_dec_ref(self.ctx.ref(), self.tactic)
def solver(self, logFile=None):
@ -8442,7 +8487,7 @@ class Probe:
return Probe(self.probe, self.ctx)
def __del__(self):
if self.probe is not None and self.ctx.ref() is not None:
if self.probe is not None and self.ctx.ref() is not None and Z3_probe_dec_ref is not None:
Z3_probe_dec_ref(self.ctx.ref(), self.probe)
def __lt__(self, other):
@ -8746,8 +8791,12 @@ def substitute(t, *m):
m = m1
if z3_debug():
_z3_assert(is_expr(t), "Z3 expression expected")
_z3_assert(all([isinstance(p, tuple) and is_expr(p[0]) and is_expr(p[1]) and p[0].sort().eq(
p[1].sort()) for p in m]), "Z3 invalid substitution, expression pairs expected.")
_z3_assert(
all([isinstance(p, tuple) and is_expr(p[0]) and is_expr(p[1]) for p in m]),
"Z3 invalid substitution, expression pairs expected.")
_z3_assert(
all([p[0].sort().eq(p[1].sort()) for p in m]),
'Z3 invalid substitution, mismatching "from" and "to" sorts.')
num = len(m)
_from = (Ast * num)()
_to = (Ast * num)()
@ -8777,6 +8826,27 @@ def substitute_vars(t, *m):
_to[i] = m[i].as_ast()
return _to_expr_ref(Z3_substitute_vars(t.ctx.ref(), t.as_ast(), num, _to), t.ctx)
def substitute_funs(t, *m):
"""Apply subistitution m on t, m is a list of pairs of a function and expression (from, to)
Every occurrence in to of the function from is replaced with the expression to.
The expression to can have free variables, that refer to the arguments of from.
For examples, see
"""
if isinstance(m, tuple):
m1 = _get_args(m)
if isinstance(m1, list) and all(isinstance(p, tuple) for p in m1):
m = m1
if z3_debug():
_z3_assert(is_expr(t), "Z3 expression expected")
_z3_assert(all([isinstance(p, tuple) and is_func_decl(p[0]) and is_expr(p[1]) for p in m]), "Z3 invalid substitution, funcion pairs expected.")
num = len(m)
_from = (FuncDecl * num)()
_to = (Ast * num)()
for i in range(num):
_from[i] = m[i][0].as_func_decl()
_to[i] = m[i][1].as_ast()
return _to_expr_ref(Z3_substitute_funs(t.ctx.ref(), t.as_ast(), num, _from, _to), t.ctx)
def Sum(*args):
"""Create the sum of the Z3 expressions.
@ -9163,7 +9233,7 @@ def parse_smt2_file(f, sorts={}, decls={}, ctx=None):
# Global default rounding mode
_dflt_rounding_mode = Z3_OP_FPA_RM_TOWARD_ZERO
_dflt_rounding_mode = Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN
_dflt_fpsort_ebits = 11
_dflt_fpsort_sbits = 53
@ -11262,7 +11332,7 @@ def user_prop_push(ctx, cb):
def user_prop_pop(ctx, cb, num_scopes):
prop = _prop_closures.get(ctx)
prop.cb = cb
pop(num_scopes)
prop.pop(num_scopes)
def user_prop_fresh(id, ctx):
@ -11308,13 +11378,13 @@ def user_prop_diseq(ctx, cb, x, y):
prop.cb = None
_user_prop_push = push_eh_type(user_prop_push)
_user_prop_pop = pop_eh_type(user_prop_pop)
_user_prop_fresh = fresh_eh_type(user_prop_fresh)
_user_prop_fixed = fixed_eh_type(user_prop_fixed)
_user_prop_final = final_eh_type(user_prop_final)
_user_prop_eq = eq_eh_type(user_prop_eq)
_user_prop_diseq = eq_eh_type(user_prop_diseq)
_user_prop_push = Z3_push_eh(user_prop_push)
_user_prop_pop = Z3_pop_eh(user_prop_pop)
_user_prop_fresh = Z3_fresh_eh(user_prop_fresh)
_user_prop_fixed = Z3_fixed_eh(user_prop_fixed)
_user_prop_final = Z3_final_eh(user_prop_final)
_user_prop_eq = Z3_eq_eh(user_prop_eq)
_user_prop_diseq = Z3_eq_eh(user_prop_diseq)
class UserPropagateBase:

View file

@ -1444,7 +1444,7 @@ Z3_DECLARE_CLOSURE(Z3_fixed_eh, void, (void* ctx, Z3_solver_callback cb, Z3_as
Z3_DECLARE_CLOSURE(Z3_eq_eh, void, (void* ctx, Z3_solver_callback cb, Z3_ast s, Z3_ast t));
Z3_DECLARE_CLOSURE(Z3_final_eh, void, (void* ctx, Z3_solver_callback cb));
Z3_DECLARE_CLOSURE(Z3_created_eh, void, (void* ctx, Z3_solver_callback cb, Z3_ast t));
Z3_DECLARE_CLOSURE(Z3_decide_eh, void, (void* ctx, Z3_solver_callback cb, Z3_ast*, unsigned*, Z3_lbool*));
Z3_DECLARE_CLOSURE(Z3_decide_eh, void, (void* ctx, Z3_solver_callback cb, Z3_ast* t, unsigned* idx, Z3_lbool* phase));
/**
@ -1685,6 +1685,14 @@ extern "C" {
*/
void Z3_API Z3_update_param_value(Z3_context c, Z3_string param_id, Z3_string param_value);
/**
\brief Retrieve description of global parameters.
def_API('Z3_get_global_param_descrs', PARAM_DESCRS, (_in(CONTEXT),))
*/
Z3_param_descrs Z3_API Z3_get_global_param_descrs(Z3_context c);
/**
\brief Interrupt the execution of a Z3 procedure.
This procedure can be used to interrupt: solvers, simplifiers and tactics.
@ -2094,6 +2102,19 @@ extern "C" {
unsigned num_constructors,
Z3_constructor constructors[]);
/**
\brief create a forward reference to a recursive datatype being declared.
The forward reference can be used in a nested occurrence: the range of an array
or as element sort of a sequence. The forward reference should only be used when
used in an accessor for a recursive datatype that gets declared.
Forward references can replace the use sort references, that are unsigned integers
in the \c Z3_mk_constructor call
def_API('Z3_mk_datatype_sort', SORT, (_in(CONTEXT), _in(SYMBOL)))
*/
Z3_sort Z3_API Z3_mk_datatype_sort(Z3_context c, Z3_symbol name);
/**
\brief Create list of constructors.
@ -2914,6 +2935,16 @@ extern "C" {
def_API('Z3_mk_repeat', AST, (_in(CONTEXT), _in(UINT), _in(AST)))
*/
Z3_ast Z3_API Z3_mk_repeat(Z3_context c, unsigned i, Z3_ast t1);
/**
\brief Extracts the bit at position \ccode{i} of a bit-vector and
yields a boolean.
The node \c t1 must have a bit-vector sort.
def_API('Z3_mk_bit2bool', AST, (_in(CONTEXT), _in(UINT), _in(AST)))
*/
Z3_ast Z3_API Z3_mk_bit2bool(Z3_context c, unsigned i, Z3_ast t1);
/**
\brief Shift left.
@ -3687,7 +3718,7 @@ extern "C" {
/**
\brief Return index of first occurrence of \c substr in \c s starting from offset \c offset.
\brief Return index of the first occurrence of \c substr in \c s starting from offset \c offset.
If \c s does not contain \c substr, then the value is -1, if \c offset is the length of \c s, then the value is -1 as well.
The value is -1 if \c offset is negative or larger than the length of \c s.
@ -3696,7 +3727,7 @@ extern "C" {
Z3_ast Z3_API Z3_mk_seq_index(Z3_context c, Z3_ast s, Z3_ast substr, Z3_ast offset);
/**
\brief Return the last occurrence of \c substr in \c s.
\brief Return index of the last occurrence of \c substr in \c s.
If \c s does not contain \c substr, then the value is -1,
def_API('Z3_mk_seq_last_index', AST, (_in(CONTEXT), _in(AST), _in(AST)))
*/
@ -4840,7 +4871,7 @@ extern "C" {
/**
\brief Return \c Z3_L_TRUE if \c a is true, \c Z3_L_FALSE if it is false, and \c Z3_L_UNDEF otherwise.
def_API('Z3_get_bool_value', INT, (_in(CONTEXT), _in(AST)))
def_API('Z3_get_bool_value', LBOOL, (_in(CONTEXT), _in(AST)))
*/
Z3_lbool Z3_API Z3_get_bool_value(Z3_context c, Z3_ast a);
@ -5261,6 +5292,20 @@ extern "C" {
unsigned num_exprs,
Z3_ast const to[]);
/**
\brief Substitute funcions in \c from with new expressions in \c to.
The expressions in \c to can have free variables. The free variable in \c to at index 0
refers to the first argument of \c from, the free variable at index 1 corresponds to the second argument.
def_API('Z3_substitute_funs', AST, (_in(CONTEXT), _in(AST), _in(UINT), _in_array(2, FUNC_DECL), _in_array(2, AST)))
*/
Z3_ast Z3_API Z3_substitute_funs(Z3_context c,
Z3_ast a,
unsigned num_funs,
Z3_func_decl const from[],
Z3_ast const to[]);
/**
\brief Translate/Copy the AST \c a from context \c source to context \c target.
AST \c a must have been created using context \c source.
@ -6710,6 +6755,8 @@ extern "C" {
\param push_eh - a callback invoked when scopes are pushed
\param pop_eh - a callback invoked when scopes are poped
\param fresh_eh - a solver may spawn new solvers internally. This callback is used to produce a fresh user_context to be associated with fresh solvers.
def_API('Z3_solver_propagate_init', VOID, (_in(CONTEXT), _in(SOLVER), _in(VOID_PTR), _fnptr(Z3_push_eh), _fnptr(Z3_pop_eh), _fnptr(Z3_fresh_eh)))
*/
void Z3_API Z3_solver_propagate_init(
@ -6725,6 +6772,8 @@ extern "C" {
The supported expression types are
- Booleans
- Bit-vectors
def_API('Z3_solver_propagate_fixed', VOID, (_in(CONTEXT), _in(SOLVER), _fnptr(Z3_fixed_eh)))
*/
void Z3_API Z3_solver_propagate_fixed(Z3_context c, Z3_solver s, Z3_fixed_eh fixed_eh);
@ -6741,33 +6790,50 @@ extern "C" {
The callback context can only be accessed (for propagation and for dynamically registering expressions) within a callback.
If the callback context gets used for propagation or conflicts, those propagations take effect and
may trigger new decision variables to be set.
def_API('Z3_solver_propagate_final', VOID, (_in(CONTEXT), _in(SOLVER), _fnptr(Z3_final_eh)))
*/
void Z3_API Z3_solver_propagate_final(Z3_context c, Z3_solver s, Z3_final_eh final_eh);
/**
\brief register a callback on expression equalities.
def_API('Z3_solver_propagate_eq', VOID, (_in(CONTEXT), _in(SOLVER), _fnptr(Z3_eq_eh)))
*/
void Z3_API Z3_solver_propagate_eq(Z3_context c, Z3_solver s, Z3_eq_eh eq_eh);
/**
\brief register a callback on expression dis-equalities.
def_API('Z3_solver_propagate_diseq', VOID, (_in(CONTEXT), _in(SOLVER), _fnptr(Z3_eq_eh)))
*/
void Z3_API Z3_solver_propagate_diseq(Z3_context c, Z3_solver s, Z3_eq_eh eq_eh);
/**
* \brief register a callback when a new expression with a registered function is used by the solver
* The registered function appears at the top level and is created using \ref Z3_propagate_solver_declare.
\brief register a callback when a new expression with a registered function is used by the solver
The registered function appears at the top level and is created using \ref Z3_propagate_solver_declare.
def_API('Z3_solver_propagate_created', VOID, (_in(CONTEXT), _in(SOLVER), _fnptr(Z3_created_eh)))
*/
void Z3_API Z3_solver_propagate_created(Z3_context c, Z3_solver s, Z3_created_eh created_eh);
/**
* \brief register a callback when a the solver decides to split on a registered expression
* The callback may set passed expression to another registered expression which will be selected instead.
* In case the expression is a bitvector the bit to split on is determined by the bit argument and the
* truth-value to try first is given by is_pos
\brief register a callback when the solver decides to split on a registered expression.
The callback may set the passed expression to another registered expression which will be selected instead.
In case the expression is a bitvector the bit to split on is determined by the bit argument and the
truth-value to try first is given by is_pos. In case the truth value is undefined the solver will decide.
def_API('Z3_solver_propagate_decide', VOID, (_in(CONTEXT), _in(SOLVER), _fnptr(Z3_decide_eh)))
*/
void Z3_API Z3_solver_propagate_decide(Z3_context c, Z3_solver s, Z3_decide_eh decide_eh);
/**
Sets the next expression to split on
def_API('Z3_solver_next_split', VOID, (_in(CONTEXT), _in(SOLVER_CALLBACK), _in(AST), _in(UINT), _in(LBOOL)))
*/
void Z3_API Z3_solver_next_split(Z3_context c, Z3_solver_callback cb, Z3_ast t, unsigned idx, Z3_lbool phase);
/**
Create uninterpreted function declaration for the user propagator.
When expressions using the function are created by the solver invoke a callback
@ -6826,7 +6892,7 @@ extern "C" {
\sa Z3_solver_check_assumptions
def_API('Z3_solver_check', INT, (_in(CONTEXT), _in(SOLVER)))
def_API('Z3_solver_check', LBOOL, (_in(CONTEXT), _in(SOLVER)))
*/
Z3_lbool Z3_API Z3_solver_check(Z3_context c, Z3_solver s);
@ -6839,7 +6905,7 @@ extern "C" {
\sa Z3_solver_check
def_API('Z3_solver_check_assumptions', INT, (_in(CONTEXT), _in(SOLVER), _in(UINT), _in_array(2, AST)))
def_API('Z3_solver_check_assumptions', LBOOL, (_in(CONTEXT), _in(SOLVER), _in(UINT), _in_array(2, AST)))
*/
Z3_lbool Z3_API Z3_solver_check_assumptions(Z3_context c, Z3_solver s,
unsigned num_assumptions, Z3_ast const assumptions[]);
@ -6860,7 +6926,7 @@ extern "C" {
A side-effect of the function is a satisfiability check on the assertions on the solver that is passed in.
The function return \c Z3_L_FALSE if the current assertions are not satisfiable.
def_API('Z3_get_implied_equalities', INT, (_in(CONTEXT), _in(SOLVER), _in(UINT), _in_array(2, AST), _out_array(2, UINT)))
def_API('Z3_get_implied_equalities', LBOOL, (_in(CONTEXT), _in(SOLVER), _in(UINT), _in_array(2, AST), _out_array(2, UINT)))
*/
Z3_lbool Z3_API Z3_get_implied_equalities(Z3_context c,
Z3_solver s,
@ -6871,7 +6937,7 @@ extern "C" {
/**
\brief retrieve consequences from solver that determine values of the supplied function symbols.
def_API('Z3_solver_get_consequences', INT, (_in(CONTEXT), _in(SOLVER), _in(AST_VECTOR), _in(AST_VECTOR), _in(AST_VECTOR)))
def_API('Z3_solver_get_consequences', LBOOL, (_in(CONTEXT), _in(SOLVER), _in(AST_VECTOR), _in(AST_VECTOR), _in(AST_VECTOR)))
*/
Z3_lbool Z3_API Z3_solver_get_consequences(Z3_context c,

View file

@ -109,7 +109,7 @@ extern "C" {
- \c Z3_L_TRUE if the query is satisfiable. Obtain the answer by calling #Z3_fixedpoint_get_answer.
- \c Z3_L_UNDEF if the query was interrupted, timed out or otherwise failed.
def_API('Z3_fixedpoint_query', INT, (_in(CONTEXT), _in(FIXEDPOINT), _in(AST)))
def_API('Z3_fixedpoint_query', LBOOL, (_in(CONTEXT), _in(FIXEDPOINT), _in(AST)))
*/
Z3_lbool Z3_API Z3_fixedpoint_query(Z3_context c, Z3_fixedpoint d, Z3_ast query);
@ -123,7 +123,7 @@ extern "C" {
- \c Z3_L_TRUE if the query is satisfiable. Obtain the answer by calling #Z3_fixedpoint_get_answer.
- \c Z3_L_UNDEF if the query was interrupted, timed out or otherwise failed.
def_API('Z3_fixedpoint_query_relations', INT, (_in(CONTEXT), _in(FIXEDPOINT), _in(UINT), _in_array(2, FUNC_DECL)))
def_API('Z3_fixedpoint_query_relations', LBOOL, (_in(CONTEXT), _in(FIXEDPOINT), _in(UINT), _in_array(2, FUNC_DECL)))
*/
Z3_lbool Z3_API Z3_fixedpoint_query_relations(
Z3_context c, Z3_fixedpoint d,

View file

@ -151,7 +151,7 @@ extern "C" {
\sa Z3_optimize_get_statistics
\sa Z3_optimize_get_unsat_core
def_API('Z3_optimize_check', INT, (_in(CONTEXT), _in(OPTIMIZE), _in(UINT), _in_array(2, AST)))
def_API('Z3_optimize_check', LBOOL, (_in(CONTEXT), _in(OPTIMIZE), _in(UINT), _in_array(2, AST)))
*/
Z3_lbool Z3_API Z3_optimize_check(Z3_context c, Z3_optimize o, unsigned num_assumptions, Z3_ast const assumptions[]);

View file

@ -18,7 +18,6 @@ Notes:
--*/
#include<iostream>
#include "util/rational.h"
#include "api/z3_macros.h"

View file

@ -22,6 +22,7 @@ Notes:
#include "util/stream_buffer.h"
#include "util/symbol.h"
#include "util/trace.h"
#include<iostream>
#include<sstream>
#include<vector>

View file

@ -18,7 +18,6 @@ Notes:
--*/
#pragma once
#include<iostream>
#include "api/z3.h"
#include "util/z3_exception.h"

View file

@ -40,7 +40,7 @@ extern "C" {
- \c Z3_L_TRUE if the query is satisfiable. Obtain the answer by calling #Z3_fixedpoint_get_answer.
- \c Z3_L_UNDEF if the query was interrupted, timed out or otherwise failed.
def_API('Z3_fixedpoint_query_from_lvl', INT, (_in(CONTEXT), _in(FIXEDPOINT), _in(AST), _in(UINT)))
def_API('Z3_fixedpoint_query_from_lvl', LBOOL, (_in(CONTEXT), _in(FIXEDPOINT), _in(AST), _in(UINT)))
*/
Z3_lbool Z3_API Z3_fixedpoint_query_from_lvl (Z3_context c,Z3_fixedpoint d, Z3_ast query, unsigned lvl);

View file

@ -527,6 +527,19 @@ func_decl * array_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters
return nullptr;
}
return mk_array_ext(arity, domain, parameters[0].get_int());
case OP_ARRAY_MAXDIFF:
case OP_ARRAY_MINDIFF: {
if (num_parameters != 0)
m_manager->raise_exception("min/maxdiff don't take any parameters");
if (arity != 2 || domain[0] != domain[1] || !is_array_sort(domain[0]) || 1 != get_array_arity(domain[0]))
m_manager->raise_exception("min/maxdiff don't take two arrays of same sort and with integer index");
sort* idx = get_array_domain(domain[0], 0);
arith_util arith(*m_manager);
if (!arith.is_int(idx))
m_manager->raise_exception("min/maxdiff take integer index domain");
return m_manager->mk_func_decl(k == OP_ARRAY_MAXDIFF ? symbol("maxdiff") : symbol("mindiff"),
arity, domain, arith.mk_int(), func_decl_info(m_family_id, k));
}
case OP_ARRAY_DEFAULT:
return mk_default(arity, domain);
case OP_SET_UNION:
@ -587,6 +600,10 @@ void array_decl_plugin::get_op_names(svector<builtin_name>& op_names, symbol con
op_names.push_back(builtin_name("subset",OP_SET_SUBSET));
op_names.push_back(builtin_name("as-array", OP_AS_ARRAY));
op_names.push_back(builtin_name("array-ext", OP_ARRAY_EXT));
op_names.push_back(builtin_name("mindiff", OP_ARRAY_MINDIFF));
op_names.push_back(builtin_name("maxdiff", OP_ARRAY_MAXDIFF));
#if 0
op_names.push_back(builtin_name("set-has-size", OP_SET_HAS_SIZE));
op_names.push_back(builtin_name("card", OP_SET_CARD));
@ -613,6 +630,24 @@ bool array_decl_plugin::is_fully_interp(sort * s) const {
return m_manager->is_fully_interp(get_array_range(s));
}
bool array_decl_plugin::is_value(app * _e) const {
expr* e = _e;
array_util u(*m_manager);
while (true) {
if (u.is_const(e, e))
return m_manager->is_value(e);
if (u.is_store(e)) {
for (unsigned i = 1; i < to_app(e)->get_num_args(); ++i)
if (!m_manager->is_value(to_app(e)->get_arg(i)))
return false;
e = to_app(e)->get_arg(0);
continue;
}
return false;
}
}
func_decl * array_recognizers::get_as_array_func_decl(expr * n) const {
SASSERT(is_as_array(n));
return to_func_decl(to_app(n)->get_decl()->get_parameter(0).get_ast());
@ -687,3 +722,4 @@ func_decl* array_util::mk_array_ext(sort *domain, unsigned i) {
parameter p(i);
return m_manager.mk_func_decl(m_fid, OP_ARRAY_EXT, 1, &p, 2, domains);
}

View file

@ -45,6 +45,8 @@ enum array_op_kind {
OP_ARRAY_EXT,
OP_ARRAY_DEFAULT,
OP_ARRAY_MAP,
OP_ARRAY_MAXDIFF,
OP_ARRAY_MINDIFF,
OP_SET_UNION,
OP_SET_INTERSECT,
OP_SET_DIFFERENCE,
@ -135,6 +137,9 @@ class array_decl_plugin : public decl_plugin {
expr * get_some_value(sort * s) override;
bool is_fully_interp(sort * s) const override;
bool is_value(app * e) const override;
};
class array_recognizers {
@ -157,6 +162,8 @@ public:
bool is_complement(expr* n) const { return is_app_of(n, m_fid, OP_SET_COMPLEMENT); }
bool is_as_array(expr * n) const { return is_app_of(n, m_fid, OP_AS_ARRAY); }
bool is_as_array(expr * n, func_decl*& f) const { return is_as_array(n) && (f = get_as_array_func_decl(n), true); }
bool is_maxdiff(expr const* n) const { return is_app_of(n, m_fid, OP_ARRAY_MAXDIFF); }
bool is_mindiff(expr const* n) const { return is_app_of(n, m_fid, OP_ARRAY_MINDIFF); }
bool is_set_has_size(expr* e) const { return is_app_of(e, m_fid, OP_SET_HAS_SIZE); }
bool is_set_card(expr* e) const { return is_app_of(e, m_fid, OP_SET_CARD); }
bool is_select(func_decl* f) const { return is_decl_of(f, m_fid, OP_SELECT); }
@ -182,6 +189,8 @@ public:
bool is_store_ext(expr* e, expr_ref& a, expr_ref_vector& args, expr_ref& value);
MATCH_BINARY(is_subset);
MATCH_BINARY(is_maxdiff);
MATCH_BINARY(is_mindiff);
};
class array_util : public array_recognizers {
@ -272,6 +281,8 @@ public:
func_decl * mk_array_ext(sort* domain, unsigned i);
sort * mk_array_sort(sort* dom, sort* range) { return mk_array_sort(1, &dom, range); }
sort * mk_array_sort(sort* a, sort* b, sort* range) { sort* dom[2] = { a, b }; return mk_array_sort(2, dom, range); }
sort * mk_array_sort(sort* a, sort* b, sort* c, sort* range) { sort* dom[3] = { a, b, c}; return mk_array_sort(3, dom, range); }
sort * mk_array_sort(unsigned arity, sort* const* domain, sort* range);

View file

@ -30,6 +30,7 @@ Revision History:
#include "ast/arith_decl_plugin.h"
#include "ast/ast_translation.h"
#include "util/z3_version.h"
#include <iostream>
// -----------------------------------
@ -498,9 +499,9 @@ bool compare_nodes(ast const * n1, ast const * n2) {
template<typename T>
inline unsigned ast_array_hash(T * const * array, unsigned size, unsigned init_value) {
if (size == 0)
return init_value;
switch (size) {
case 0:
return init_value;
case 1:
return combine_hash(array[0]->hash(), init_value);
case 2:
@ -993,7 +994,7 @@ sort * basic_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, paramete
}
func_decl * basic_decl_plugin::mk_eq_decl_core(char const * name, decl_kind k, sort * s, ptr_vector<func_decl> & cache) {
unsigned id = s->get_decl_id();
unsigned id = s->get_small_id();
force_ptr_array_size(cache, id + 1);
if (cache[id] == 0) {
sort * domain[2] = { s, s};
@ -1009,7 +1010,7 @@ func_decl * basic_decl_plugin::mk_eq_decl_core(char const * name, decl_kind k, s
}
func_decl * basic_decl_plugin::mk_ite_decl(sort * s) {
unsigned id = s->get_decl_id();
unsigned id = s->get_small_id();
force_ptr_array_size(m_ite_decls, id + 1);
if (m_ite_decls[id] == 0) {
sort * domain[3] = { m_bool_sort, s, s};
@ -1672,12 +1673,12 @@ void ast_manager::add_lambda_def(func_decl* f, quantifier* q) {
}
quantifier* ast_manager::is_lambda_def(func_decl* f) {
if (f->get_info() && f->get_info()->is_lambda()) {
if (f->get_info() && f->get_info()->is_lambda())
return m_lambda_defs[f];
}
return nullptr;
}
void ast_manager::register_plugin(family_id id, decl_plugin * plugin) {
SASSERT(m_plugins.get(id, 0) == 0);
m_plugins.setx(id, plugin, 0);
@ -2413,7 +2414,7 @@ bool ast_manager::is_pattern(expr const * n, ptr_vector<expr> &args) {
static void trace_quant(std::ostream& strm, quantifier* q) {
strm << (is_lambda(q) ? "[mk-lambda]" : "[mk-quant]")
<< " #" << q->get_id() << " " << q->get_qid() << " " << q->get_num_decls();
<< " #" << q->get_id() << " " << ensure_quote(q->get_qid()) << " " << q->get_num_decls();
for (unsigned i = 0; i < q->get_num_patterns(); ++i) {
strm << " #" << q->get_pattern(i)->get_id();
}

View file

@ -272,6 +272,7 @@ public:
family_id get_family_id() const { return m_family_id; }
decl_kind get_decl_kind() const { return m_kind; }
bool is_decl_of(family_id fid, decl_kind k) const { return m_family_id == fid && k == m_kind; }
unsigned get_num_parameters() const { return m_parameters.size(); }
parameter const & get_parameter(unsigned idx) const { return m_parameters[idx]; }
parameter const * get_parameters() const { return m_parameters.begin(); }
@ -572,11 +573,12 @@ protected:
decl(ast_kind k, symbol const & name, decl_info * info):ast(k), m_name(name), m_info(info) {}
public:
unsigned get_decl_id() const { SASSERT(get_id() >= c_first_decl_id); return get_id() - c_first_decl_id; }
unsigned get_small_id() const { SASSERT(get_id() >= c_first_decl_id); return get_id() - c_first_decl_id; }
symbol const & get_name() const { return m_name; }
decl_info * get_info() const { return m_info; }
family_id get_family_id() const { return m_info == nullptr ? null_family_id : m_info->get_family_id(); }
decl_kind get_decl_kind() const { return m_info == nullptr ? null_decl_kind : m_info->get_decl_kind(); }
bool is_decl_of(family_id fid, decl_kind k) const { return m_info && m_info->is_decl_of(fid, k); }
unsigned get_num_parameters() const { return m_info == nullptr ? 0 : m_info->get_num_parameters(); }
parameter const & get_parameter(unsigned idx) const { return m_info->get_parameter(idx); }
parameter const * get_parameters() const { return m_info == nullptr ? nullptr : m_info->get_parameters(); }
@ -671,6 +673,9 @@ protected:
public:
sort* get_sort() const;
unsigned get_small_id() const { return get_id(); }
};
// -----------------------------------
@ -715,7 +720,7 @@ public:
unsigned get_num_parameters() const { return get_decl()->get_num_parameters(); }
parameter const& get_parameter(unsigned idx) const { return get_decl()->get_parameter(idx); }
parameter const* get_parameters() const { return get_decl()->get_parameters(); }
bool is_app_of(family_id fid, decl_kind k) const { return get_family_id() == fid && get_decl_kind() == k; }
bool is_app_of(family_id fid, decl_kind k) const { return m_decl->is_decl_of(fid, k); }
unsigned get_num_args() const { return m_num_args; }
expr * get_arg(unsigned idx) const { SASSERT(idx < m_num_args); return m_args[idx]; }
expr * const * get_args() const { return m_args; }
@ -1615,7 +1620,7 @@ public:
bool is_lambda_def(quantifier* q) const { return q->get_qid() == m_lambda_def; }
void add_lambda_def(func_decl* f, quantifier* q);
quantifier* is_lambda_def(func_decl* f);
quantifier* is_lambda_def(app* e) { return is_lambda_def(e->get_decl()); }
symbol const& lambda_def_qid() const { return m_lambda_def; }
@ -1914,9 +1919,8 @@ public:
return mk_fresh_const(prefix.c_str(), s, skolem);
}
app * mk_fresh_const(symbol const& prefix, sort * s, bool skolem = true) {
auto str = prefix.str();
return mk_fresh_const(str.c_str(), s, skolem);
app * mk_fresh_const(symbol const& prefix, sort * s, bool skolem = true) {
return mk_const(mk_fresh_func_decl(prefix, symbol::null, 0, nullptr, s, skolem));
}
symbol mk_fresh_var_name(char const * prefix = nullptr);
@ -2574,7 +2578,7 @@ typedef ast_ref_fast_mark2 expr_ref_fast_mark2;
when N is deleted.
*/
class ast_mark {
struct decl2uint { unsigned operator()(decl const & d) const { return d.get_decl_id(); } };
struct decl2uint { unsigned operator()(decl const & d) const { return d.get_small_id(); } };
obj_mark<expr> m_expr_marks;
obj_mark<decl, bit_vector, decl2uint> m_decl_marks;
public:

View file

@ -17,7 +17,7 @@ Revision History:
--*/
#include<iostream>
#include "ast/ast_ll_pp.h"
#include "ast/for_each_ast.h"
#include "ast/arith_decl_plugin.h"
#include "ast/datatype_decl_plugin.h"

View file

@ -19,7 +19,7 @@ Revision History:
#pragma once
#include "ast/ast.h"
#include<iostream>
#include<ostream>
void ast_ll_pp(std::ostream & out, ast_manager & m, ast * n, bool only_exprs=true, bool compact=true);
void ast_ll_pp(std::ostream & out, ast_manager & m, ast * n, ast_mark & visited, bool only_exprs=true, bool compact=true);

View file

@ -6,7 +6,7 @@ Abstract: Pretty-printer for proofs in Graphviz format
#pragma once
#include <iostream>
#include <ostream>
#include "ast/ast_pp.h"
class ast_pp_dot {

View file

@ -27,9 +27,8 @@ void ast_pp_util::collect(expr* e) {
}
void ast_pp_util::collect(unsigned n, expr* const* es) {
for (unsigned i = 0; i < n; ++i) {
for (unsigned i = 0; i < n; ++i)
coll.visit(es[i]);
}
}
void ast_pp_util::collect(expr_ref_vector const& es) {
@ -38,31 +37,31 @@ void ast_pp_util::collect(expr_ref_vector const& es) {
void ast_pp_util::display_decls(std::ostream& out) {
ast_smt_pp pp(m);
bool first = m_num_decls == 0;
coll.order_deps(m_num_sorts);
coll.order_deps(m_sorts);
unsigned n = coll.get_num_sorts();
ast_mark seen;
for (unsigned i = m_num_sorts; i < n; ++i)
for (unsigned i = m_sorts; i < n; ++i)
pp.display_sort_decl(out, coll.get_sorts()[i], seen);
m_num_sorts = n;
m_sorts = n;
n = coll.get_num_decls();
for (unsigned i = m_num_decls; i < n; ++i) {
for (unsigned i = m_decls; i < n; ++i) {
func_decl* f = coll.get_func_decls()[i];
if (f->get_family_id() == null_family_id && !m_removed.contains(f)) {
if (f->get_family_id() == null_family_id && !m_removed.contains(f))
ast_smt2_pp(out, f, m_env) << "\n";
}
}
m_num_decls = n;
if (first) {
vector<std::pair<func_decl*, expr*>> recfuns;
recfun::util u(m);
func_decl_ref_vector funs = u.get_rec_funs();
if (funs.empty()) return;
for (func_decl * f : funs) {
recfuns.push_back(std::make_pair(f, u.get_def(f).get_rhs()));
}
m_decls = n;
n = coll.get_rec_decls().size();
vector<std::pair<func_decl*, expr*>> recfuns;
recfun::util u(m);
for (unsigned i = m_rec_decls; i < n; ++i) {
func_decl* f = coll.get_rec_decls()[i];
recfuns.push_back(std::make_pair(f, u.get_def(f).get_rhs()));
}
if (!recfuns.empty())
ast_smt2_pp_recdefs(out, recfuns, m_env);
}
m_rec_decls = n;
}
void ast_pp_util::remove_decl(func_decl* f) {
@ -116,14 +115,14 @@ void ast_pp_util::display_asserts(std::ostream& out, expr_ref_vector const& fmls
void ast_pp_util::push() {
coll.push();
m_num_sorts_trail.push_back(m_num_sorts);
m_num_decls_trail.push_back(m_num_decls);
m_rec_decls.push();
m_decls.push();
m_sorts.push();
}
void ast_pp_util::pop(unsigned n) {
coll.pop(n);
m_num_sorts = m_num_sorts_trail[m_num_sorts_trail.size() - n];
m_num_decls = m_num_decls_trail[m_num_decls_trail.size() - n];
m_num_sorts_trail.shrink(m_num_sorts_trail.size() - n);
m_num_decls_trail.shrink(m_num_decls_trail.size() - n);
m_rec_decls.pop(n);
m_decls.pop(n);
m_sorts.pop(n);
}

View file

@ -21,21 +21,23 @@ Revision History:
#include "ast/decl_collector.h"
#include "ast/ast_smt2_pp.h"
#include "util/obj_hashtable.h"
#include "util/stacked_value.h"
class ast_pp_util {
ast_manager& m;
obj_hashtable<func_decl> m_removed;
smt2_pp_environment_dbg m_env;
unsigned m_num_sorts, m_num_decls;
unsigned_vector m_num_sorts_trail, m_num_decls_trail;
public:
stacked_value<unsigned> m_rec_decls;
stacked_value<unsigned> m_decls;
stacked_value<unsigned> m_sorts;
public:
decl_collector coll;
ast_pp_util(ast_manager& m): m(m), m_env(m), m_num_sorts(0), m_num_decls(0), coll(m) {}
ast_pp_util(ast_manager& m): m(m), m_env(m), m_rec_decls(0), m_decls(0), m_sorts(0), coll(m) {}
void reset() { coll.reset(); m_removed.reset(); m_num_sorts = 0; m_num_decls = 0; }
void reset() { coll.reset(); m_removed.reset(); m_sorts.clear(0u); m_decls.clear(0u); m_rec_decls.clear(0u); }
void collect(expr* e);
@ -57,7 +59,7 @@ class ast_pp_util {
std::ostream& display_expr(std::ostream& out, expr* f, bool neat = true);
void push();
void pop(unsigned n);
smt2_pp_environment& env() { return m_env; }

View file

@ -18,6 +18,7 @@ Revision History:
--*/
#include "ast/ast_printer.h"
#include "ast/pp.h"
#include <iostream>
class simple_ast_printer_context : public ast_printer_context {
ast_manager & m_manager;
@ -51,3 +52,6 @@ public:
ast_printer_context * mk_simple_ast_printer_context(ast_manager & m) {
return alloc(simple_ast_printer_context, m);
}
std::ostream & ast_printer_context::regular_stream() { return std::cout; }
std::ostream & ast_printer_context::diagnostic_stream() { return std::cerr; }

View file

@ -20,6 +20,7 @@ Revision History:
#include "ast/ast.h"
#include "ast/ast_smt2_pp.h"
#include <ostream>
class ast_printer {
public:
@ -46,8 +47,8 @@ class ast_printer_context : public ast_printer {
public:
~ast_printer_context() override {}
virtual ast_manager & get_ast_manager() = 0;
virtual std::ostream & regular_stream() { return std::cout; }
virtual std::ostream & diagnostic_stream() { return std::cerr; }
virtual std::ostream & regular_stream();
virtual std::ostream & diagnostic_stream();
};

View file

@ -1401,6 +1401,7 @@ std::ostream& operator<<(std::ostream& out, sort_ref_vector const& e) {
#ifdef Z3DEBUG
#include <iostream>
void pp(expr const * n, ast_manager & m) {
std::cout << mk_ismt2_pp(const_cast<expr*>(n), m) << std::endl;
}

View file

@ -20,7 +20,7 @@ Revision History:
--*/
#include<sstream>
#include<iostream>
#include<ostream>
#include "util/vector.h"
#include "util/smt2_util.h"
#include "ast/ast_smt_pp.h"

View file

@ -423,7 +423,8 @@ func_decl * bv_decl_plugin::mk_num_decl(unsigned num_parameters, parameter const
// This cannot be enforced now, since some Z3 modules try to generate these invalid numerals.
// After SMT-COMP, I should find all offending modules.
// For now, I will just simplify the numeral here.
parameter p0(mod(parameters[0].get_rational(), rational::power_of_two(bv_size)));
rational v = parameters[0].get_rational();
parameter p0(mod2k(v, bv_size));
parameter ps[2] = { std::move(p0), parameters[1] };
sort * bv = get_bv_sort(bv_size);
return m_manager->mk_const_decl(m_bv_sym, bv, func_decl_info(m_family_id, OP_BV_NUM, num_parameters, ps));
@ -641,16 +642,18 @@ void bv_decl_plugin::get_offset_term(app * a, expr * & t, rational & offset) con
offset = decl->get_parameter(0).get_rational();
sz = decl->get_parameter(1).get_int();
t = a->get_arg(1);
offset = mod(offset, rational::power_of_two(sz));
offset = mod2k(offset, sz);
}
else {
t = a;
offset = rational(0);
offset.reset();
}
}
bool bv_decl_plugin::are_distinct(app * a, app * b) const {
#if 1
if (decl_plugin::are_distinct(a, b))
return true;
// Check for a + k1 != a + k2 when k1 != k2
rational a_offset;
expr * a_term;
@ -665,8 +668,7 @@ bool bv_decl_plugin::are_distinct(app * a, app * b) const {
tout << "b: " << b_offset << " + " << mk_ismt2_pp(b_term, *m_manager) << "\n";);
if (a_term == b_term && a_offset != b_offset)
return true;
#endif
return decl_plugin::are_distinct(a, b);
return false;
}
void bv_decl_plugin::get_sort_names(svector<builtin_name> & sort_names, symbol const & logic) {
@ -752,9 +754,9 @@ expr * bv_decl_plugin::get_some_value(sort * s) {
}
rational bv_recognizers::norm(rational const & val, unsigned bv_size, bool is_signed) const {
rational r = mod(val, rational::power_of_two(bv_size));
rational r = mod2k(val, bv_size);
SASSERT(!r.is_neg());
if (is_signed) {
if (is_signed) {
if (r >= rational::power_of_two(bv_size - 1)) {
r -= rational::power_of_two(bv_size);
}

View file

@ -19,6 +19,7 @@ Revision History:
#include "util/warning.h"
#include "ast/array_decl_plugin.h"
#include "ast/seq_decl_plugin.h"
#include "ast/datatype_decl_plugin.h"
#include "ast/ast_smt2_pp.h"
#include "ast/ast_pp.h"
@ -462,28 +463,27 @@ namespace datatype {
}
for (symbol const& s : m_def_block) {
def& d = *m_defs[s];
for (constructor* c : d) {
for (accessor* a : *c) {
for (constructor* c : d)
for (accessor* a : *c)
a->fix_range(sorts);
}
}
}
if (!u().is_well_founded(sorts.size(), sorts.data())) {
if (!u().is_well_founded(sorts.size(), sorts.data()))
m_manager->raise_exception("datatype is not well-founded");
}
if (!u().is_covariant(sorts.size(), sorts.data())) {
if (!u().is_covariant(sorts.size(), sorts.data()))
m_manager->raise_exception("datatype is not co-variant");
}
array_util autil(m);
seq_util sutil(m);
sort* sr;
for (sort* s : sorts) {
for (constructor const* c : get_def(s)) {
for (accessor const* a : *c) {
if (autil.is_array(a->range())) {
if (sorts.contains(get_array_range(a->range()))) {
m_has_nested_arrays = true;
}
}
if (autil.is_array(a->range()) && sorts.contains(get_array_range(a->range())))
m_has_nested_rec = true;
else if (sutil.is_seq(a->range(), sr) && sorts.contains(sr))
m_has_nested_rec = true;
else if (sutil.is_re(a->range(), sr) && sorts.contains(sr))
m_has_nested_rec = true;
}
}
}
@ -1103,12 +1103,19 @@ namespace datatype {
return r;
}
bool util::is_recursive_array(sort* a) {
bool util::is_recursive_nested(sort* a) {
array_util autil(m);
if (!autil.is_array(a))
return false;
a = autil.get_array_range_rec(a);
return is_datatype(a) && is_recursive(a);
seq_util sutil(m);
sort* sr;
if (autil.is_array(a)) {
a = autil.get_array_range_rec(a);
return is_datatype(a) && is_recursive(a);
}
if (sutil.is_seq(a, sr))
return is_datatype(sr) && is_recursive(sr);
if (sutil.is_re(a, sr))
return is_datatype(sr) && is_recursive(sr);
return false;
}
bool util::is_enum_sort(sort* s) {
@ -1273,14 +1280,22 @@ namespace datatype {
*/
bool util::are_siblings(sort * s1, sort * s2) {
array_util autil(m);
s1 = autil.get_array_range_rec(s1);
s2 = autil.get_array_range_rec(s2);
if (!is_datatype(s1) || !is_datatype(s2)) {
seq_util sutil(m);
auto get_nested = [&](sort* s) {
while (true) {
if (autil.is_array(s))
s = get_array_range(s);
else if (!sutil.is_seq(s, s))
break;
}
return s;
};
s1 = get_nested(s1);
s2 = get_nested(s2);
if (!is_datatype(s1) || !is_datatype(s2))
return s1 == s2;
}
else {
else
return get_def(s1).id() == get_def(s2).id();
}
}
unsigned util::get_datatype_num_constructors(sort * ty) {

View file

@ -207,14 +207,14 @@ namespace datatype {
unsigned m_id_counter;
svector<symbol> m_def_block;
unsigned m_class_id;
mutable bool m_has_nested_arrays;
mutable bool m_has_nested_rec;
void inherit(decl_plugin* other_p, ast_translation& tr) override;
void log_axiom_definitions(symbol const& s, sort * new_sort);
public:
plugin(): m_id_counter(0), m_class_id(0), m_has_nested_arrays(false) {}
plugin(): m_id_counter(0), m_class_id(0), m_has_nested_rec(false) {}
~plugin() override;
void finalize() override;
@ -254,7 +254,7 @@ namespace datatype {
unsigned get_axiom_base_id(symbol const& s) { return m_axiom_bases[s]; }
util & u() const;
bool has_nested_arrays() const { return m_has_nested_arrays; }
bool has_nested_rec() const { return m_has_nested_rec; }
private:
bool is_value_visit(bool unique, expr * arg, ptr_buffer<app> & todo) const;
@ -334,7 +334,7 @@ namespace datatype {
bool is_datatype(sort const* s) const { return is_sort_of(s, fid(), DATATYPE_SORT); }
bool is_enum_sort(sort* s);
bool is_recursive(sort * ty);
bool is_recursive_array(sort * ty);
bool is_recursive_nested(sort * ty);
bool is_constructor(func_decl * f) const { return is_decl_of(f, fid(), OP_DT_CONSTRUCTOR); }
bool is_recognizer(func_decl * f) const { return is_recognizer0(f) || is_is(f); }
bool is_recognizer0(func_decl * f) const { return is_decl_of(f, fid(), OP_DT_RECOGNISER); }
@ -365,7 +365,7 @@ namespace datatype {
func_decl * get_accessor_constructor(func_decl * accessor);
func_decl * get_recognizer_constructor(func_decl * recognizer) const;
func_decl * get_update_accessor(func_decl * update) const;
bool has_nested_arrays() const { return plugin().has_nested_arrays(); }
bool has_nested_rec() const { return plugin().has_nested_rec(); }
family_id get_family_id() const { return fid(); }
decl::plugin& plugin() const;
bool are_siblings(sort * s1, sort * s2);

View file

@ -19,6 +19,7 @@ Revision History:
--*/
#include "ast/decl_collector.h"
#include "ast/ast_pp.h"
#include "ast/recfun_decl_plugin.h"
void decl_collector::visit_sort(sort * n) {
SASSERT(!m_visited.is_marked(n));
@ -49,8 +50,12 @@ bool decl_collector::is_bool(sort * s) {
void decl_collector::visit_func(func_decl * n) {
if (!m_visited.is_marked(n)) {
family_id fid = n->get_family_id();
if (fid == null_family_id) {
if (fid == null_family_id)
m_decls.push_back(n);
else if (fid == m_rec_fid) {
m_rec_decls.push_back(n);
recfun::util u(m());
m_todo.push_back(u.get_def(n).get_rhs());
}
m_visited.mark(n, true);
m_trail.push_back(n);
@ -63,6 +68,8 @@ decl_collector::decl_collector(ast_manager & m):
m_dt_util(m) {
m_basic_fid = m_manager.get_basic_family_id();
m_dt_fid = m_dt_util.get_family_id();
recfun::util rec_util(m);
m_rec_fid = rec_util.get_family_id();
}
void decl_collector::visit(ast* n) {
@ -143,9 +150,8 @@ void decl_collector::collect_deps(sort* s, sort_set& set) {
set.insert(s);
if (s->is_sort_of(m_dt_util.get_family_id(), DATATYPE_SORT)) {
unsigned num_sorts = m_dt_util.get_datatype_num_parameter_sorts(s);
for (unsigned i = 0; i < num_sorts; ++i) {
for (unsigned i = 0; i < num_sorts; ++i)
set.insert(m_dt_util.get_datatype_parameter_sort(s, i));
}
unsigned num_cnstr = m_dt_util.get_datatype_num_constructors(s);
for (unsigned i = 0; i < num_cnstr; i++) {
func_decl * cnstr = m_dt_util.get_datatype_constructors(s)->get(i);
@ -157,29 +163,27 @@ void decl_collector::collect_deps(sort* s, sort_set& set) {
for (unsigned i = s->get_num_parameters(); i-- > 0; ) {
parameter const& p = s->get_parameter(i);
if (p.is_ast() && is_sort(p.get_ast())) {
if (p.is_ast() && is_sort(p.get_ast()))
set.insert(to_sort(p.get_ast()));
}
}
}
void decl_collector::push() {
m_trail_lim.push_back(m_trail.size());
m_sorts_lim.push_back(m_sorts.size());
m_decls_lim.push_back(m_decls.size());
m_sorts.push_scope();
m_decls.push_scope();
m_rec_decls.push_scope();
}
void decl_collector::pop(unsigned n) {
SASSERT(n > 0);
unsigned sz = m_trail_lim[m_trail_lim.size() - n];
for (unsigned i = m_trail.size(); i-- > sz; ) {
for (unsigned i = m_trail.size(); i-- > sz; )
m_visited.mark(m_trail.get(i), false);
}
m_trail.shrink(sz);
m_sorts.shrink(m_sorts_lim[m_sorts_lim.size() - n]);
m_decls.shrink(m_decls_lim[m_decls_lim.size() - n]);
m_trail_lim.shrink(m_trail_lim.size() - n);
m_sorts_lim.shrink(m_sorts_lim.size() - n);
m_decls_lim.shrink(m_decls_lim.size() - n);
m_sorts.pop_scope(n);
m_decls.pop_scope(n);
m_rec_decls.pop_scope(n);
}

View file

@ -20,21 +20,22 @@ Revision History:
#pragma once
#include "util/top_sort.h"
#include "util/lim_vector.h"
#include "ast/ast.h"
#include "ast/datatype_decl_plugin.h"
class decl_collector {
ast_manager & m_manager;
ptr_vector<sort> m_sorts;
ptr_vector<func_decl> m_decls;
lim_svector<sort*> m_sorts;
lim_svector<func_decl*> m_decls;
lim_svector<func_decl*> m_rec_decls;
ast_mark m_visited;
ast_ref_vector m_trail;
unsigned_vector m_trail_lim;
unsigned_vector m_sorts_lim;
unsigned_vector m_decls_lim;
family_id m_basic_fid;
family_id m_dt_fid;
datatype_util m_dt_util;
family_id m_rec_fid;
ptr_vector<ast> m_todo;
void visit_sort(sort* n);
@ -64,7 +65,8 @@ public:
unsigned get_num_sorts() const { return m_sorts.size(); }
unsigned get_num_decls() const { return m_decls.size(); }
ptr_vector<sort> const& get_sorts() const { return m_sorts; }
ptr_vector<func_decl> const& get_func_decls() const { return m_decls; }
lim_svector<sort*> const& get_sorts() const { return m_sorts; }
lim_svector<func_decl*> const& get_func_decls() const { return m_decls; }
lim_svector<func_decl*> const& get_rec_decls() const { return m_rec_decls; }
};

View file

@ -700,16 +700,8 @@ namespace datalog {
}
bool dl_decl_util::is_numeral_ext(expr* e, uint64_t& v) const {
if (is_numeral(e, v)) {
if (is_numeral(e, v))
return true;
}
rational val;
unsigned bv_size = 0;
if (bv().is_numeral(e, val, bv_size) && bv_size < 64) {
SASSERT(val.is_uint64());
v = val.get_uint64();
return true;
}
if (m.is_true(e)) {
v = 1;
return true;
@ -718,16 +710,43 @@ namespace datalog {
v = 0;
return true;
}
rational val;
unsigned bv_size = 0;
if (bv().is_numeral(e, val, bv_size) && bv_size < 64) {
SASSERT(val.is_uint64());
v = val.get_uint64();
return true;
}
datatype::util dt(m);
if (dt.is_enum_sort(e->get_sort()) && dt.is_constructor(e)) {
auto& cs = *dt.get_datatype_constructors(e->get_sort());
v = 0;
for (func_decl* f : cs) {
if (f == to_app(e)->get_decl())
return true;
++v;
}
}
return false;
}
bool dl_decl_util::is_numeral_ext(expr* c) const {
if (is_numeral(c)) return true;
if (is_numeral(c))
return true;
rational val;
unsigned bv_size = 0;
if (arith().is_numeral(c, val) && val.is_uint64()) return true;
if (bv().is_numeral(c, val, bv_size) && bv_size < 64) return true;
return m.is_true(c) || m.is_false(c);
if (arith().is_numeral(c, val) && val.is_uint64())
return true;
if (bv().is_numeral(c, val, bv_size) && bv_size < 64)
return true;
if (m.is_true(c) || m.is_false(c))
return true;
datatype::util dt(m);
if (dt.is_enum_sort(c->get_sort()) && dt.is_constructor(c))
return true;
return false;
}
sort* dl_decl_util::mk_sort(const symbol& name, uint64_t domain_size) {

View file

@ -30,7 +30,7 @@ namespace euf {
m_nodes.push_back(n);
m_exprs.push_back(f);
if (is_app(f) && num_args > 0) {
unsigned id = to_app(f)->get_decl()->get_decl_id();
unsigned id = to_app(f)->get_decl()->get_small_id();
m_decl2enodes.reserve(id+1);
m_decl2enodes[id].push_back(n);
}
@ -60,7 +60,7 @@ namespace euf {
enode_vector const& egraph::enodes_of(func_decl* f) {
unsigned id = f->get_decl_id();
unsigned id = f->get_small_id();
if (id < m_decl2enodes.size())
return m_decl2enodes[id];
return m_empty_enodes;
@ -341,7 +341,7 @@ namespace euf {
m_expr2enode[e->get_id()] = nullptr;
n->~enode();
if (is_app(e) && n->num_args() > 0)
m_decl2enodes[to_app(e)->get_decl()->get_decl_id()].pop_back();
m_decl2enodes[to_app(e)->get_decl()->get_small_id()].pop_back();
m_nodes.pop_back();
m_exprs.pop_back();
};

View file

@ -162,7 +162,7 @@ namespace euf {
bool merge_tf() const { return merge_enabled() && (class_size() > 1 || num_parents() > 0 || num_args() > 0); }
enode* get_arg(unsigned i) const { SASSERT(i < num_args()); return m_args[i]; }
unsigned hash() const { return m_expr->hash(); }
unsigned hash() const { return m_expr->get_id(); }
unsigned get_table_id() const { return m_table_id; }
void set_table_id(unsigned t) { m_table_id = t; }
@ -204,6 +204,8 @@ namespace euf {
app* get_app() const { return to_app(m_expr); }
func_decl* get_decl() const { return is_app(m_expr) ? to_app(m_expr)->get_decl() : nullptr; }
unsigned get_expr_id() const { return m_expr->get_id(); }
unsigned get_id() const { return m_expr->get_id(); }
unsigned get_small_id() const { return m_expr->get_small_id(); }
unsigned get_root_id() const { return m_root->m_expr->get_id(); }
bool children_are_roots() const;
enode* get_next() const { return m_next; }

View file

@ -162,7 +162,7 @@ expr_ref bv2fpa_converter::convert_bv2rm(expr * bv_rm) {
}
}
else {
std::cout << expr_ref(bv_rm, m) << " not converted\n";
//std::cout << expr_ref(bv_rm, m) << " not converted\n";
}
return res;
@ -312,17 +312,29 @@ func_interp * bv2fpa_converter::convert_func_interp(model_core * mc, func_decl *
}
}
auto fid = m_fpa_util.get_family_id();
expr_ref_vector dom(m);
for (unsigned i = 0; i < f->get_arity(); ++i)
dom.push_back(m.mk_var(i, f->get_domain(i)));
if (m_fpa_util.is_to_sbv(f) || m_fpa_util.is_to_ubv(f)) {
auto fid = m_fpa_util.get_family_id();
auto k = m_fpa_util.is_to_sbv(f) ? OP_FPA_TO_SBV_I : OP_FPA_TO_UBV_I;
expr_ref_vector dom(m);
for (unsigned i = 0; i < f->get_arity(); ++i)
dom.push_back(m.mk_var(i, f->get_domain(i)));
parameter param = f->get_parameter(0);
func_decl_ref to_bv_i(m.mk_func_decl(fid, k, 1, &param, dom.size(), dom.data()), m);
expr_ref else_value(m.mk_app(to_bv_i, dom.size(), dom.data()), m);
result->set_else(else_value);
}
else if (m_fpa_util.is_to_real(f)) {
expr_ref_vector dom(m);
func_decl_ref to_real_i(m.mk_func_decl(fid, OP_FPA_TO_REAL_I, 0, NULL, dom.size(), dom.data()), m);
expr_ref else_value(m.mk_app(to_real_i, dom.size(), dom.data()), m);
result->set_else(else_value);
}
else if (m_fpa_util.is_to_ieee_bv(f)) {
func_decl_ref to_ieee_bv_i(m.mk_func_decl(fid, OP_FPA_TO_IEEE_BV_I, 0, NULL, dom.size(), dom.data()), m);
expr_ref else_value(m.mk_app(to_ieee_bv_i, dom.size(), dom.data()), m);
result->set_else(else_value);
}
else if (bv_fi->get_else()) {
expr_ref ft_els = rebuild_floats(mc, rng, bv_fi->get_else());
m_th_rw(ft_els);

View file

@ -52,7 +52,6 @@ fpa2bv_converter::~fpa2bv_converter() {
void fpa2bv_converter::mk_eq(expr * a, expr * b, expr_ref & result) {
if (is_float(a) && is_float(b)) {
TRACE("fpa2bv", tout << "mk_eq a=" << mk_ismt2_pp(a, m) << std::endl;
tout << "mk_eq b=" << mk_ismt2_pp(b, m) << std::endl;);
@ -95,7 +94,16 @@ void fpa2bv_converter::mk_eq(expr * a, expr * b, expr_ref & result) {
}
void fpa2bv_converter::mk_ite(expr * c, expr * t, expr * f, expr_ref & result) {
if (m_util.is_fp(t) && m_util.is_fp(f)) {
expr* c2 = nullptr, *t2 = nullptr, *f2 = nullptr;
if (m.is_ite(t, c2, t2, f2)) {
mk_ite(c2, t2, f2, result);
mk_ite(c, result, f, result);
}
else if (m.is_ite(f, c2, t2, f2)) {
mk_ite(c2, t2, f2, result);
mk_ite(c, t, result, result);
}
else if (m_util.is_fp(t) && m_util.is_fp(f)) {
expr_ref t_sgn(m), t_sig(m), t_exp(m);
expr_ref f_sgn(m), f_sig(m), f_exp(m);
split_fp(t, t_sgn, t_exp, t_sig);
@ -115,8 +123,11 @@ void fpa2bv_converter::mk_ite(expr * c, expr * t, expr * f, expr_ref & result) {
m_simp.mk_ite(c, to_app(t)->get_arg(0), to_app(f)->get_arg(0), result);
result = m_util.mk_bv2rm(result);
}
else
else {
//std::cout << mk_pp(t, m) << " " << mk_pp(f, m) << "\n";
UNREACHABLE();
}
}
void fpa2bv_converter::mk_distinct(func_decl * f, unsigned num, expr * const * args, expr_ref & result) {
@ -2655,7 +2666,8 @@ void fpa2bv_converter::mk_to_fp_real(func_decl * f, sort * s, expr * rm, expr *
unsigned sbits = m_util.get_sbits(s);
if (m_bv_util.is_numeral(bv_rm) && m_util.au().is_numeral(x)) {
rational tmp_rat; unsigned sz;
rational tmp_rat;
unsigned sz;
m_bv_util.is_numeral(to_expr(bv_rm), tmp_rat, sz);
SASSERT(tmp_rat.is_int32());
SASSERT(sz == 3);
@ -2739,9 +2751,9 @@ void fpa2bv_converter::mk_to_fp_real(func_decl * f, sort * s, expr * rm, expr *
mk_bias(unbiased_exp, exp);
v4 = m_util.mk_fp(sgn, exp, sig);
sgn = m_bv_util.mk_numeral((m_util.fm().sgn(v_tp)) ? 1 : 0, 1);
sig = m_bv_util.mk_numeral(m_util.fm().sig(v_tp), sbits - 1);
unbiased_exp = m_bv_util.mk_numeral(m_util.fm().exp(v_tp), ebits);
sgn = m_bv_util.mk_numeral((m_util.fm().sgn(v_tz)) ? 1 : 0, 1);
sig = m_bv_util.mk_numeral(m_util.fm().sig(v_tz), sbits - 1);
unbiased_exp = m_bv_util.mk_numeral(m_util.fm().exp(v_tz), ebits);
mk_bias(unbiased_exp, exp);
result = m_util.mk_fp(sgn, exp, sig);
@ -3248,6 +3260,12 @@ void fpa2bv_converter::mk_to_ieee_bv_unspecified(func_decl * f, unsigned num, ex
SASSERT(is_well_sorted(m, result));
}
void fpa2bv_converter::mk_to_ieee_bv_i(func_decl * f, unsigned num, expr * const * args, expr_ref & result)
{
func_decl_ref fu(m.mk_func_decl(f->get_family_id(), OP_FPA_TO_IEEE_BV, 0, nullptr, num, args), m);
mk_to_bv(f, num, args, true, result);
}
void fpa2bv_converter::mk_to_bv(func_decl * f, unsigned num, expr * const * args, bool is_signed, expr_ref & result) {
TRACE("fpa2bv_to_bv", for (unsigned i = 0; i < num; i++)
tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;);
@ -3478,6 +3496,11 @@ void fpa2bv_converter::mk_to_real_unspecified(func_decl * f, unsigned num, expr
}
}
void fpa2bv_converter::mk_to_real_i(func_decl * f, unsigned num, expr * const * args, expr_ref & result) {
func_decl_ref fu(m.mk_func_decl(f->get_family_id(), OP_FPA_TO_REAL, 0, nullptr, num, args), m);
mk_to_real(f, num, args, result);
}
void fpa2bv_converter::mk_fp(func_decl * f, unsigned num, expr * const * args, expr_ref & result) {
SASSERT(num == 3);
SASSERT(m_bv_util.get_bv_size(args[0]) == 1);
@ -3487,7 +3510,6 @@ void fpa2bv_converter::mk_fp(func_decl * f, unsigned num, expr * const * args, e
TRACE("fpa2bv_mk_fp", tout << "mk_fp result = " << mk_ismt2_pp(result, m) << std::endl;);
}
void fpa2bv_converter::split_fp(expr * e, expr_ref & sgn, expr_ref & exp, expr_ref & sig) const {
expr* e_sgn = nullptr, *e_exp = nullptr, *e_sig = nullptr;
VERIFY(m_util.is_fp(e, e_sgn, e_exp, e_sig));

View file

@ -136,6 +136,7 @@ public:
void mk_to_fp_unsigned(func_decl * f, unsigned num, expr * const * args, expr_ref & result);
void mk_to_ieee_bv(func_decl * f, unsigned num, expr * const * args, expr_ref & result);
void mk_to_ieee_bv_unspecified(func_decl * f, unsigned num, expr * const * args, expr_ref & result);
void mk_to_ieee_bv_i(func_decl * f, unsigned num, expr * const * args, expr_ref & result);
void mk_to_fp_real(func_decl * f, sort * s, expr * rm, expr * x, expr_ref & result);
void mk_to_fp_real_int(func_decl * f, unsigned num, expr * const * args, expr_ref & result);
@ -145,6 +146,7 @@ public:
void mk_to_sbv_i(func_decl * f, unsigned num, expr * const * args, expr_ref & result);
void mk_to_bv_unspecified(func_decl * f, unsigned num, expr * const * args, expr_ref & result);
void mk_to_real(func_decl * f, unsigned num, expr * const * args, expr_ref & result);
void mk_to_real_i(func_decl * f, unsigned num, expr * const * args, expr_ref & result);
void mk_to_real_unspecified(func_decl * f, unsigned num, expr * const * args, expr_ref & result);
void set_unspecified_fp_hi(bool v) { m_hi_fp_unspecified = v; }
@ -245,6 +247,6 @@ class fpa2bv_converter_wrapped : public fpa2bv_converter {
expr* bv2rm_value(expr* b);
expr* bv2fpa_value(sort* s, expr* a, expr* b = nullptr, expr* c = nullptr);
};

View file

@ -149,7 +149,9 @@ br_status fpa2bv_rewriter_cfg::reduce_app(func_decl * f, unsigned num, expr * co
case OP_FPA_TO_UBV_I: m_conv.mk_to_ubv_i(f, num, args, result); return BR_DONE;
case OP_FPA_TO_SBV_I: m_conv.mk_to_sbv_i(f, num, args, result); return BR_DONE;
case OP_FPA_TO_REAL: m_conv.mk_to_real(f, num, args, result); return BR_DONE;
case OP_FPA_TO_REAL_I: m_conv.mk_to_real_i(f, num, args, result); return BR_DONE;
case OP_FPA_TO_IEEE_BV: m_conv.mk_to_ieee_bv(f, num, args, result); return BR_DONE;
case OP_FPA_TO_IEEE_BV_I: m_conv.mk_to_ieee_bv_i(f, num, args, result); return BR_DONE;
case OP_FPA_BVWRAP:
case OP_FPA_BV2RM:
@ -290,15 +292,15 @@ expr_ref fpa2bv_rewriter::convert_atom(th_rewriter& rw, expr * e) {
expr_ref fpa2bv_rewriter::convert_term(th_rewriter& rw, expr * e) {
SASSERT(fu().is_rm(e) || fu().is_float(e));
ast_manager& m = m_cfg.m();
expr_ref e_conv(m), res(m);
proof_ref pr(m);
(*this)(e, e_conv);
TRACE("t_fpa_detail", tout << "term: " << mk_ismt2_pp(e, m) << std::endl;
tout << "converted term: " << mk_ismt2_pp(e_conv, m) << std::endl;);
if (fu().is_rm(e)) {
SASSERT(fu().is_bv2rm(e_conv));
expr_ref bv_rm(m);
@ -316,7 +318,7 @@ expr_ref fpa2bv_rewriter::convert_term(th_rewriter& rw, expr * e) {
}
else
UNREACHABLE();
return res;
}
@ -333,7 +335,7 @@ expr_ref fpa2bv_rewriter::convert(th_rewriter& rw, expr * e) {
ast_manager& m = m_cfg.m();
expr_ref res(m);
TRACE("t_fpa", tout << "converting " << mk_ismt2_pp(e, m) << std::endl;);
if (fu().is_fp(e))
res = e;
else if (m.is_bool(e))
@ -342,10 +344,10 @@ expr_ref fpa2bv_rewriter::convert(th_rewriter& rw, expr * e) {
res = convert_term(rw, e);
else
res = convert_conversion_term(rw, e);
TRACE("t_fpa_detail", tout << "converted; caching:" << std::endl;
tout << mk_ismt2_pp(e, m) << std::endl << " -> " << std::endl <<
mk_ismt2_pp(res, m) << std::endl;);
return res;
}

View file

@ -784,12 +784,14 @@ func_decl * fpa_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters,
case OP_FPA_TO_SBV_I:
return mk_to_sbv(k, num_parameters, parameters, arity, domain, range);
case OP_FPA_TO_REAL:
case OP_FPA_TO_REAL_I:
return mk_to_real(k, num_parameters, parameters, arity, domain, range);
case OP_FPA_TO_FP:
return mk_to_fp(k, num_parameters, parameters, arity, domain, range);
case OP_FPA_TO_FP_UNSIGNED:
return mk_to_fp_unsigned(k, num_parameters, parameters, arity, domain, range);
case OP_FPA_TO_IEEE_BV:
case OP_FPA_TO_IEEE_BV_I:
return mk_to_ieee_bv(k, num_parameters, parameters, arity, domain, range);
case OP_FPA_BVWRAP:
@ -857,6 +859,7 @@ void fpa_decl_plugin::get_op_names(svector<builtin_name> & op_names, symbol cons
op_names.push_back(builtin_name("fp.to_ubv_I", OP_FPA_TO_UBV_I));
op_names.push_back(builtin_name("fp.to_sbv_I", OP_FPA_TO_SBV_I));
op_names.push_back(builtin_name("fp.to_real", OP_FPA_TO_REAL));
op_names.push_back(builtin_name("fp.to_real_I", OP_FPA_TO_REAL_I));
op_names.push_back(builtin_name("to_fp", OP_FPA_TO_FP));
op_names.push_back(builtin_name("to_fp_unsigned", OP_FPA_TO_FP_UNSIGNED));
@ -864,6 +867,7 @@ void fpa_decl_plugin::get_op_names(svector<builtin_name> & op_names, symbol cons
/* Extensions */
op_names.push_back(builtin_name("to_ieee_bv", OP_FPA_TO_IEEE_BV));
op_names.push_back(builtin_name("fp.to_ieee_bv", OP_FPA_TO_IEEE_BV));
op_names.push_back(builtin_name("fp.to_ieee_bv_I", OP_FPA_TO_IEEE_BV_I));
}
void fpa_decl_plugin::get_sort_names(svector<builtin_name> & sort_names, symbol const & logic) {
@ -1074,7 +1078,8 @@ bool fpa_util::is_considered_uninterpreted(func_decl * f, unsigned n, expr* cons
if (f->get_family_id() != ffid)
return false;
if (is_decl_of(f, ffid, OP_FPA_TO_IEEE_BV)) {
if (is_decl_of(f, ffid, OP_FPA_TO_IEEE_BV) ||
is_decl_of(f, ffid, OP_FPA_TO_IEEE_BV_I)) {
SASSERT(n == 1);
expr* x = args[0];
return is_nan(x);
@ -1101,7 +1106,8 @@ bool fpa_util::is_considered_uninterpreted(func_decl * f, unsigned n, expr* cons
else
return mpqm.is_neg(r) || mpqm.bitsize(r) > bv_sz;
}
else if (is_decl_of(f, ffid, OP_FPA_TO_REAL)) {
else if (is_decl_of(f, ffid, OP_FPA_TO_REAL) ||
is_decl_of(f, ffid, OP_FPA_TO_REAL_I)) {
SASSERT(n == 1);
expr* x = args[0];
return is_nan(x) || is_inf(x);

View file

@ -83,12 +83,14 @@ enum fpa_op_kind {
OP_FPA_TO_UBV,
OP_FPA_TO_SBV,
OP_FPA_TO_REAL,
OP_FPA_TO_REAL_I,
OP_FPA_TO_SBV_I,
OP_FPA_TO_UBV_I,
/* Extensions */
OP_FPA_TO_IEEE_BV,
OP_FPA_TO_IEEE_BV_I,
OP_FPA_BVWRAP,
OP_FPA_BV2RM,
@ -353,11 +355,13 @@ public:
bool is_bv2rm(expr const * e) const { return is_app_of(e, get_family_id(), OP_FPA_BV2RM); }
bool is_to_ubv(expr const * e) const { return is_app_of(e, get_family_id(), OP_FPA_TO_UBV); }
bool is_to_sbv(expr const * e) const { return is_app_of(e, get_family_id(), OP_FPA_TO_SBV); }
bool is_to_real(expr const * e) const { return is_app_of(e, get_family_id(), OP_FPA_TO_REAL); }
bool is_bvwrap(func_decl const * f) const { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_BVWRAP; }
bool is_bv2rm(func_decl const * f) const { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_BV2RM; }
bool is_to_ubv(func_decl const * f) const { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_TO_UBV; }
bool is_to_sbv(func_decl const * f) const { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_TO_SBV; }
bool is_to_real(func_decl const * f) const { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_TO_REAL; }
bool is_to_ieee_bv(func_decl const * f) const { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_TO_IEEE_BV; }
bool contains_floats(ast * a);

View file

@ -70,12 +70,12 @@ public:
proof_checker(ast_manager& m);
void set_dump_lemmas(char const * logic = "AUFLIA") { m_dump_lemmas = true; m_logic = logic; }
bool check(proof* p, expr_ref_vector& side_conditions);
bool check_arith_literal(bool is_pos, app* lit, rational const& coeff, expr_ref& sum, bool& is_strict);
private:
bool check1(proof* p, expr_ref_vector& side_conditions);
bool check1_basic(proof* p, expr_ref_vector& side_conditions);
bool check1_spc(proof* p, expr_ref_vector& side_conditions);
bool check_arith_proof(proof* p);
bool check_arith_literal(bool is_pos, app* lit, rational const& coeff, expr_ref& sum, bool& is_strict);
bool match_fact(proof const* p, expr*& fact) const;
void add_premise(proof* p);
bool match_proof(proof const* p) const;

Some files were not shown because too many files have changed in this diff Show more