mirror of
https://github.com/Z3Prover/z3
synced 2025-04-24 01:25:31 +00:00
Added ml component
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
parent
454fa7dcdd
commit
bcca613cb2
60 changed files with 40332 additions and 16 deletions
607
ml/z3.idl
Normal file
607
ml/z3.idl
Normal file
|
@ -0,0 +1,607 @@
|
|||
/*++
|
||||
Copyright (c) Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
Z3
|
||||
|
||||
Abstract:
|
||||
|
||||
OCaml API for Z3.
|
||||
|
||||
The following design is used for the treatment of reference counting:
|
||||
|
||||
- The conversion of Z3_context from ML to C remembers the Z3 context, and
|
||||
registers a finalizer using Gc.finalize that calls Z3_del_context.
|
||||
|
||||
- The conversion of Z3_ast and other reference counted types from C to ML:
|
||||
|
||||
+ stores the last translated context with the Z3_ast in the wrapper
|
||||
object;
|
||||
|
||||
+ registers a finalizer using Gc.finalize that decrements the reference
|
||||
counter of the Z3_ast;
|
||||
|
||||
+ increments the reference count of the Z3_ast.
|
||||
|
||||
The finalizers are registered using (the C interface to) Gc.finalize,
|
||||
not attaching a finalizer to a custom block. The finalizers registered
|
||||
by Gc.finalize are guaranteed to be called in reverse
|
||||
registration-order, which is necessary to ensure that Z3_context's are
|
||||
finalized only after all the Z3_ast's within them.
|
||||
|
||||
- ML Z3.ast (and subtypes) are given generic hash and comparison
|
||||
operations using Z3_get_ast_hash and Z3_get_ast_id. Other types could
|
||||
be handled similarly if analogous hash and id operations were exported
|
||||
by the C API.
|
||||
|
||||
- The wrapper for Z3_mk_context is customized (using quote(call,...) in
|
||||
z3_api.patched.h) to call Z3_mk_context_rc, and the ML API does not
|
||||
include mk_context_rc.
|
||||
|
||||
This scheme relies on the property that all reference counted values
|
||||
returned from C to ML are in the Z3_context that was last sent from ML to
|
||||
C. This is normally straightforward, but note that it depends on the
|
||||
argument order of e.g. the Z3_*translate functions.
|
||||
|
||||
Non-reference counted Z3 types that have delete operations have finalizers
|
||||
that call the delete operations. The exposed delete operations are
|
||||
shadowed by nop functions. The types whose delete operation accepts a
|
||||
context use Gc.finalize while those that do not use custom block
|
||||
finalizers.
|
||||
|
||||
Custom c2ml functions check the Z3 error code prior to allocating ML
|
||||
values or registering finalizers. Other functions check the Z3 error code
|
||||
after making a Z3 library call.
|
||||
|
||||
Some operations return NULL pointers when operations fail, or accept NULL
|
||||
pointers. To handle these cases Z3_{ast,func_interp,sort}_opt types are
|
||||
introduced. These are synonyms of Z3_{ast,func_interp,sort} but are
|
||||
translated into OCaml option types. If the NULL pointers were passed to
|
||||
ML, even if the user does not access them, they will have finalizers
|
||||
registered, so when they die the OCaml GC will crash trying to call
|
||||
dec_ref on NULL.
|
||||
|
||||
There is an alternate implementation, enabled by setting LEAK_CONTEXTS,
|
||||
that avoids the overhead of Gc.finalize finalizers, but at the price of
|
||||
leaking Z3_context objects.
|
||||
|
||||
Notes:
|
||||
|
||||
OCaml does not support unsigned types, so CamlIDL conflates signed and
|
||||
unsigned types of the same size. Therefore, functions in the C API
|
||||
operating on unsigned values that become redundant after this conflation
|
||||
are excluded from the ML API using [#ifndef CAMLIDL] in z3_api.h.
|
||||
|
||||
CamlIDL does not support function pointers, so functions in the C API with
|
||||
function pointer arguments are handled manually.
|
||||
|
||||
Author:
|
||||
|
||||
Jakob Lichtenberg (JakobL) 2007-08-08
|
||||
Josh Berdine (jjb) 2012-03-21
|
||||
|
||||
--*/
|
||||
|
||||
|
||||
// cpp trick to include expanded macro arguments in string literals
|
||||
#define xstr(s) str(s)
|
||||
#define str(s) #s
|
||||
quote(c,"#define xstr(s) str(s)");
|
||||
quote(c,"#define str(s) #s");
|
||||
|
||||
|
||||
// CamlIDL (1.05) has a bug where it does not accept [unsigned] as a type,
|
||||
// only as a specifier, so unsigned is defined to be unsigned int.
|
||||
#define unsigned unsigned int
|
||||
|
||||
|
||||
// Suppress "warning C4090: 'function' : different 'const' qualifiers" as
|
||||
// CamlIDL does not seem to get this right.
|
||||
quote(c,"#pragma warning(disable:4090)");
|
||||
|
||||
|
||||
#ifndef MLAPIV3
|
||||
|
||||
#define DEFINE_TYPE(T) typedef [abstract] void* T
|
||||
#define DEFINE_VOID(T) typedef [abstract] void* T
|
||||
|
||||
#define BEGIN_MLAPI_EXCLUDE quote(mli,"(*");
|
||||
#define END_MLAPI_EXCLUDE quote(mli,"*)");
|
||||
|
||||
#ifdef LEAK_CONTEXTS
|
||||
|
||||
// Declare pointer type with custom conversion functions.
|
||||
#define DEFINE_CUST_TYPE(T) \
|
||||
typedef [abstract, ml2c(ml2c_Z3_ ## T), c2ml(c2ml_Z3_ ## T)] void* Z3_ ## T
|
||||
|
||||
#else
|
||||
|
||||
// Declare pointer type with custom conversion functions.
|
||||
// Register an OCaml closure that just calls a C finalization function.
|
||||
#define DEFINE_CUST_TYPE(T) \
|
||||
quote(ml,xstr(\
|
||||
external finalize_Z3_ ## T : T -> unit = xstr(finalize_Z3_ ## T);; \
|
||||
let _ = Callback.register xstr(finalize_Z3_ ## T) finalize_Z3_ ## T \
|
||||
)); \
|
||||
typedef [abstract, ml2c(ml2c_Z3_ ## T), c2ml(c2ml_Z3_ ## T)] void* Z3_ ## T
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// Z3_context
|
||||
quote(c,"
|
||||
void check_error_code (Z3_context c);
|
||||
|
||||
Z3_context last_ctx;
|
||||
");
|
||||
|
||||
#ifdef LEAK_CONTEXTS
|
||||
|
||||
quote(c,"
|
||||
value c2ml_Z3_context(Z3_context* c)
|
||||
{
|
||||
value v;
|
||||
v = caml_alloc_small(1, Abstract_tag);
|
||||
*((Z3_context *) Bp_val(v)) = *c;
|
||||
return v;
|
||||
}
|
||||
|
||||
void ml2c_Z3_context(value v, Z3_context* c)
|
||||
{
|
||||
*c = *((Z3_context *) Bp_val(v));
|
||||
last_ctx = *c;
|
||||
}
|
||||
");
|
||||
|
||||
#else
|
||||
|
||||
quote(c,"
|
||||
// caml_final_register is the implementation of Gc.finalize
|
||||
value caml_final_register (value f, value v);
|
||||
|
||||
void register_finalizer(value** closure, char* name, Z3_context ctx, value v)
|
||||
{
|
||||
if (*closure == NULL) {
|
||||
*closure = caml_named_value(name);
|
||||
if (*closure == NULL) {
|
||||
Z3_set_error(ctx, Z3_INTERNAL_FATAL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
caml_final_register(**closure, v);
|
||||
}
|
||||
|
||||
value c2ml_Z3_context (Z3_context* c)
|
||||
{
|
||||
static value* finalize_Z3_context_closure = NULL;
|
||||
value v;
|
||||
v = caml_alloc_small(1, Abstract_tag);
|
||||
Field(v, 0) = (value) *c;
|
||||
register_finalizer(&finalize_Z3_context_closure, \"finalize_Z3_context\",
|
||||
(Z3_context) *c, v);
|
||||
return v;
|
||||
}
|
||||
|
||||
void ml2c_Z3_context (value v, Z3_context* c)
|
||||
{
|
||||
*c = (Z3_context) Field(v, 0);
|
||||
last_ctx = *c;
|
||||
}
|
||||
|
||||
value finalize_Z3_context (value v)
|
||||
{
|
||||
Z3_context c;
|
||||
c = (Z3_context) Field(v, 0);
|
||||
Z3_del_context(c);
|
||||
return Val_unit;
|
||||
}
|
||||
");
|
||||
|
||||
#endif
|
||||
|
||||
DEFINE_CUST_TYPE(context);
|
||||
|
||||
|
||||
// Z3_symbol
|
||||
typedef [abstract] void* Z3_symbol;
|
||||
|
||||
|
||||
// Z3_ast: reference counted type with hashing and comparison
|
||||
quote(c,"
|
||||
typedef struct _Z3_ast_context {
|
||||
Z3_ast ast;
|
||||
Z3_context ctx;
|
||||
} Z3_ast_context;
|
||||
|
||||
void ml2c_Z3_ast (value v, Z3_ast* c)
|
||||
{
|
||||
*c = ((Z3_ast_context*) Data_custom_val(v))->ast;
|
||||
}
|
||||
|
||||
static int compare_Z3_ast (value v1, value v2)
|
||||
{
|
||||
Z3_ast_context* ac1;
|
||||
Z3_ast_context* ac2;
|
||||
unsigned id1, id2;
|
||||
ac1 = Data_custom_val(v1);
|
||||
ac2 = Data_custom_val(v2);
|
||||
id1 = Z3_get_ast_id(ac1->ctx, ac1->ast);
|
||||
check_error_code(ac1->ctx);
|
||||
id2 = Z3_get_ast_id(ac2->ctx, ac2->ast);
|
||||
check_error_code(ac2->ctx);
|
||||
return id2 - id1;
|
||||
}
|
||||
|
||||
static intnat hash_Z3_ast (value v)
|
||||
{
|
||||
Z3_ast_context* ac;
|
||||
unsigned hash;
|
||||
ac = Data_custom_val(v);
|
||||
hash = Z3_get_ast_hash(ac->ctx, ac->ast);
|
||||
check_error_code(ac->ctx);
|
||||
return hash;
|
||||
}
|
||||
");
|
||||
|
||||
#ifdef LEAK_CONTEXTS
|
||||
|
||||
quote(c,"
|
||||
static void finalize_Z3_ast (value v)
|
||||
{
|
||||
Z3_ast_context* ac;
|
||||
ac = Data_custom_val(v);
|
||||
Z3_dec_ref(ac->ctx, ac->ast);
|
||||
check_error_code(ac->ctx);
|
||||
}
|
||||
|
||||
static struct custom_operations cops_Z3_ast = {
|
||||
NULL,
|
||||
finalize_Z3_ast,
|
||||
compare_Z3_ast,
|
||||
hash_Z3_ast,
|
||||
custom_serialize_default,
|
||||
custom_deserialize_default
|
||||
};
|
||||
|
||||
value c2ml_Z3_ast (Z3_ast* c)
|
||||
{
|
||||
value v;
|
||||
Z3_ast_context* ac;
|
||||
check_error_code(last_ctx);
|
||||
v = alloc_custom(&cops_Z3_ast, sizeof(Z3_ast_context), 0, 1);
|
||||
ac = Data_custom_val(v);
|
||||
ac->ctx = last_ctx;
|
||||
ac->ast = *c;
|
||||
Z3_inc_ref(ac->ctx, ac->ast);
|
||||
return v;
|
||||
}
|
||||
");
|
||||
|
||||
#else
|
||||
|
||||
quote(c,"
|
||||
value finalize_Z3_ast (value v)
|
||||
{
|
||||
Z3_ast_context* ac;
|
||||
ac = Data_custom_val(v);
|
||||
Z3_dec_ref(ac->ctx, ac->ast);
|
||||
check_error_code(ac->ctx);
|
||||
return Val_unit;
|
||||
}
|
||||
|
||||
static struct custom_operations cops_Z3_ast = {
|
||||
NULL,
|
||||
custom_finalize_default,
|
||||
compare_Z3_ast,
|
||||
hash_Z3_ast,
|
||||
custom_serialize_default,
|
||||
custom_deserialize_default
|
||||
};
|
||||
|
||||
value c2ml_Z3_ast (Z3_ast* c)
|
||||
{
|
||||
static value* finalize_Z3_ast_closure = NULL;
|
||||
value v;
|
||||
Z3_ast_context* ac;
|
||||
check_error_code(last_ctx);
|
||||
v = caml_alloc_custom(&cops_Z3_ast, sizeof(Z3_ast_context), 0, 1);
|
||||
ac = Data_custom_val(v);
|
||||
ac->ast = *c;
|
||||
ac->ctx = last_ctx;
|
||||
register_finalizer(&finalize_Z3_ast_closure, \"finalize_Z3_ast\",
|
||||
(Z3_context) *c, v);
|
||||
Z3_inc_ref(last_ctx, *c);
|
||||
return v;
|
||||
}
|
||||
");
|
||||
|
||||
#endif
|
||||
|
||||
DEFINE_CUST_TYPE(ast);
|
||||
|
||||
|
||||
// subtypes of Z3_ast
|
||||
quote(c,"\
|
||||
#define DEFINE_SUBAST_OPS(T) \
|
||||
void ml2c_ ## T (value v, T * a) \
|
||||
{ \
|
||||
ml2c_Z3_ast(v, (Z3_ast*) a); \
|
||||
} \
|
||||
\
|
||||
value c2ml_ ## T (T * a) \
|
||||
{ \
|
||||
return c2ml_Z3_ast((Z3_ast*) a); \
|
||||
} \
|
||||
");
|
||||
|
||||
#define DEFINE_SUBAST(T) \
|
||||
typedef [mltype("private ast"), ml2c(ml2c_ ## T), c2ml(c2ml_ ## T)] Z3_ast T
|
||||
|
||||
quote(c,"DEFINE_SUBAST_OPS(Z3_sort)"); DEFINE_SUBAST(Z3_sort);
|
||||
quote(c,"DEFINE_SUBAST_OPS(Z3_func_decl)"); DEFINE_SUBAST(Z3_func_decl);
|
||||
quote(c,"DEFINE_SUBAST_OPS(Z3_app)"); DEFINE_SUBAST(Z3_app);
|
||||
quote(c,"DEFINE_SUBAST_OPS(Z3_pattern)"); DEFINE_SUBAST(Z3_pattern);
|
||||
|
||||
|
||||
// reference counted types without hashing and comparison
|
||||
#ifdef LEAK_CONTEXTS
|
||||
|
||||
quote(c,"\
|
||||
#define DEFINE_RC_OPS(T) \
|
||||
typedef struct _ ## T ## _context { \
|
||||
T dat; \
|
||||
Z3_context ctx; \
|
||||
} T ## _context; \
|
||||
\
|
||||
static void finalize_ ## T (value v) \
|
||||
{ \
|
||||
T ## _context* ac; \
|
||||
ac = Data_custom_val(v); \
|
||||
T ## _dec_ref(ac->ctx, ac->dat); \
|
||||
check_error_code(ac->ctx); \
|
||||
} \
|
||||
\
|
||||
static struct custom_operations cops_ ## T = { \
|
||||
NULL, \
|
||||
finalize_ ## T, \
|
||||
custom_compare_default, \
|
||||
custom_hash_default, \
|
||||
custom_serialize_default, \
|
||||
custom_deserialize_default \
|
||||
}; \
|
||||
\
|
||||
value c2ml_ ## T (T * c) \
|
||||
{ \
|
||||
value v; \
|
||||
T ## _context* ac; \
|
||||
check_error_code(last_ctx); \
|
||||
v = alloc_custom(&cops_ ## T, sizeof(T ## _context), 0, 1); \
|
||||
ac = Data_custom_val(v); \
|
||||
ac->dat = *c; \
|
||||
ac->ctx = last_ctx; \
|
||||
T ## _inc_ref(ac->ctx, ac->dat); \
|
||||
return v; \
|
||||
} \
|
||||
\
|
||||
void ml2c_ ## T (value v, T * c) \
|
||||
{ \
|
||||
*c = ((T ## _context*) Data_custom_val(v))->dat; \
|
||||
} \
|
||||
");
|
||||
|
||||
#else
|
||||
|
||||
quote(c,"\
|
||||
#define DEFINE_RC_OPS(T) \
|
||||
value c2ml_ ## T (T * c) \
|
||||
{ \
|
||||
static value* finalize_ ## T ## _closure = NULL; \
|
||||
value v; \
|
||||
check_error_code(last_ctx); \
|
||||
v = caml_alloc_small(2, Abstract_tag); \
|
||||
Field(v, 0) = (value) *c; \
|
||||
Field(v, 1) = (value) last_ctx; \
|
||||
register_finalizer(&finalize_ ## T ## _closure, xstr(finalize_ ## T), \
|
||||
(Z3_context) *c, v); \
|
||||
T ## _inc_ref(last_ctx, *c); \
|
||||
return v; \
|
||||
} \
|
||||
\
|
||||
void ml2c_ ## T (value v, T * c) \
|
||||
{ \
|
||||
*c = (T) Field(v, 0); \
|
||||
} \
|
||||
\
|
||||
value finalize_ ## T (value v) \
|
||||
{ \
|
||||
Z3_context c; \
|
||||
c = (Z3_context) Field(v, 1); \
|
||||
T ## _dec_ref(c, (T) Field(v, 0)); \
|
||||
check_error_code(c); \
|
||||
return Val_unit; \
|
||||
} \
|
||||
");
|
||||
|
||||
#endif
|
||||
|
||||
quote(c,"DEFINE_RC_OPS(Z3_params)"); DEFINE_CUST_TYPE(params);
|
||||
quote(c,"DEFINE_RC_OPS(Z3_param_descrs)"); DEFINE_CUST_TYPE(param_descrs);
|
||||
quote(c,"DEFINE_RC_OPS(Z3_model)"); DEFINE_CUST_TYPE(model);
|
||||
quote(c,"DEFINE_RC_OPS(Z3_func_interp)"); DEFINE_CUST_TYPE(func_interp);
|
||||
quote(c,"DEFINE_RC_OPS(Z3_func_entry)"); DEFINE_CUST_TYPE(func_entry);
|
||||
quote(c,"DEFINE_RC_OPS(Z3_fixedpoint)"); DEFINE_CUST_TYPE(fixedpoint);
|
||||
quote(c,"DEFINE_RC_OPS(Z3_ast_vector)"); DEFINE_CUST_TYPE(ast_vector);
|
||||
quote(c,"DEFINE_RC_OPS(Z3_ast_map)"); DEFINE_CUST_TYPE(ast_map);
|
||||
quote(c,"DEFINE_RC_OPS(Z3_goal)"); DEFINE_CUST_TYPE(goal);
|
||||
quote(c,"DEFINE_RC_OPS(Z3_tactic)"); DEFINE_CUST_TYPE(tactic);
|
||||
quote(c,"DEFINE_RC_OPS(Z3_probe)"); DEFINE_CUST_TYPE(probe);
|
||||
quote(c,"DEFINE_RC_OPS(Z3_apply_result)"); DEFINE_CUST_TYPE(apply_result);
|
||||
quote(c,"DEFINE_RC_OPS(Z3_solver)"); DEFINE_CUST_TYPE(solver);
|
||||
quote(c,"DEFINE_RC_OPS(Z3_stats)"); DEFINE_CUST_TYPE(stats);
|
||||
|
||||
|
||||
// possibly-NULL pointer types, translated to OCaml option types
|
||||
quote(c,"\
|
||||
#define DEFINE_OPT_OPS(T) \
|
||||
void ml2c_ ## T ## _opt (value v, T* c) \
|
||||
{ \
|
||||
struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; \
|
||||
camlidl_ctx _ctx = &_ctxs; \
|
||||
if (v != Val_int(0)) { \
|
||||
camlidl_ml2c_z3_ ## T(Field(v, 0), c, _ctx); \
|
||||
} else { \
|
||||
*c = NULL; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
value c2ml_ ## T ## _opt (T* c) \
|
||||
{ \
|
||||
struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; \
|
||||
camlidl_ctx _ctx = &_ctxs; \
|
||||
value v; \
|
||||
value a; \
|
||||
if (*c) { \
|
||||
a = camlidl_c2ml_z3_ ## T(c, _ctx); \
|
||||
Begin_root(a) \
|
||||
v = caml_alloc_small(1, 0); \
|
||||
Field(v, 0) = a; \
|
||||
End_roots(); \
|
||||
} else { \
|
||||
v = Val_int(0); \
|
||||
} \
|
||||
return v; \
|
||||
}
|
||||
");
|
||||
|
||||
#define DEFINE_OPT_TYPE(T) \
|
||||
typedef [mltype(xstr(T option)), \
|
||||
ml2c(ml2c_Z3_ ## T ## _opt), \
|
||||
c2ml(c2ml_Z3_ ## T ## _opt) \
|
||||
] Z3_ ## T Z3_ ## T ## _opt
|
||||
|
||||
quote(c,"DEFINE_OPT_OPS(Z3_ast)");
|
||||
DEFINE_OPT_TYPE(ast);
|
||||
|
||||
quote(c,"DEFINE_OPT_OPS(Z3_sort)");
|
||||
DEFINE_OPT_TYPE(sort);
|
||||
|
||||
quote(c,"DEFINE_OPT_OPS(Z3_func_interp)");
|
||||
DEFINE_OPT_TYPE(func_interp);
|
||||
|
||||
|
||||
// ToDo: these unnecessarily appear in the API documentation
|
||||
DEFINE_TYPE(Z3_constructor);
|
||||
DEFINE_TYPE(Z3_constructor_list);
|
||||
|
||||
|
||||
// shadow delete operations with nops
|
||||
quote(ml,"
|
||||
let del_constructor _ _ = ()
|
||||
let del_constructor_list _ _ = ()
|
||||
let del_model _ _ = ()
|
||||
let del_context _ = ()
|
||||
let reset_memory () = ()
|
||||
");
|
||||
|
||||
|
||||
#else // MLAPIV3
|
||||
|
||||
// Provide custom error handler:
|
||||
quote (c,"Z3_error_handler caml_z3_error_handler;");
|
||||
quote (c,"void caml_z3_error_handler(Z3_context c, Z3_error_code e) { static char buffer[128]; char * msg = Z3_get_error_msg_ex(c, e); if (strlen(msg) > 100) { failwith(\"Z3: error message is too big to fit in buffer\"); } else { sprintf(buffer, \"Z3: %s\", msg); failwith(buffer); } }");
|
||||
|
||||
|
||||
#define DEFINE_TYPE(T) typedef [abstract] void* T
|
||||
#define DEFINE_VOID(T) typedef [abstract] void* T
|
||||
|
||||
#define BEGIN_MLAPI_EXCLUDE
|
||||
#define END_MLAPI_EXCLUDE
|
||||
|
||||
#endif // MLAPIV3
|
||||
|
||||
|
||||
|
||||
#ifndef __in
|
||||
#define __in [in]
|
||||
#endif
|
||||
|
||||
#ifndef __out
|
||||
#define __out [out]
|
||||
#endif
|
||||
|
||||
#ifndef __out_opt
|
||||
#define __out_opt [out,unique]
|
||||
#endif
|
||||
|
||||
#ifndef __ecount
|
||||
#define __ecount(num_args) [NOT_SUPPORTED]
|
||||
#endif
|
||||
|
||||
#ifndef __in_ecount
|
||||
#define __in_ecount(num_args) [in, size_is(num_args), length_is(num_args)]
|
||||
#endif
|
||||
|
||||
#ifndef __out_ecount
|
||||
#define __out_ecount(num_args) [out, size_is(num_args), length_is(num_args)]
|
||||
#endif
|
||||
|
||||
#ifndef __inout_ecount
|
||||
#define __inout_ecount(num_args) [in, out, size_is(num_args), length_is(num_args)]
|
||||
#endif
|
||||
|
||||
#ifndef __inout
|
||||
#define __inout [in, out]
|
||||
#endif
|
||||
|
||||
#ifndef Z3_bool_opt
|
||||
#define Z3_bool_opt void
|
||||
#endif
|
||||
|
||||
|
||||
#define Z3_API
|
||||
|
||||
#ifdef MLAPIV3
|
||||
|
||||
#include "z3V3_api.idl"
|
||||
#include "x3V3.mli"
|
||||
#include "x3V3.ml"
|
||||
|
||||
#else
|
||||
|
||||
#include "z3_api.idl"
|
||||
#include "x3.ml"
|
||||
|
||||
quote(ml,"
|
||||
let _ =
|
||||
Printexc.register_printer (function
|
||||
| Error(c,e) -> Some (\"Z3 \"^(get_error_msg c e))
|
||||
| _ -> None
|
||||
)
|
||||
");
|
||||
|
||||
|
||||
quote(mli,"
|
||||
(**
|
||||
{2 {L Legacy V3 API}}
|
||||
*)
|
||||
|
||||
module V3 : sig
|
||||
(**
|
||||
{2 {L Legacy V3 API}}
|
||||
*)
|
||||
");
|
||||
|
||||
quote(ml,"
|
||||
module V3 = struct
|
||||
");
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MLAPIV3
|
||||
|
||||
quote(mlmli,"
|
||||
end
|
||||
");
|
||||
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue