3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-07 18:05:21 +00:00
z3/lib/smtparser.cpp
Leonardo de Moura e9eab22e5c Z3 sources
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
2012-10-02 11:35:25 -07:00

5321 lines
179 KiB
C++

/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
smtparser.cpp
Abstract:
SMT parser into ast.
Author:
Nikolaj Bjorner (nbjorner) 2006-10-4.
Leonardo de Moura (leonardo)
Revision History:
--*/
#include<iostream>
#include<istream>
#include<fstream>
#include<sstream>
#include<signal.h>
#include"region.h"
#include"scanner.h"
#include"symbol.h"
#include"vector.h"
#include"symbol_table.h"
#include"smtlib.h"
#include"smtparser.h"
#include"ast_pp.h"
#include"bv_decl_plugin.h"
#include"array_decl_plugin.h"
#include"datatype_decl_plugin.h"
#include"warning.h"
#include"error_codes.h"
#include"pattern_validation.h"
#include"unifier.h"
#include"kbo.h"
#include"lpo.h"
#include"substitution_tree.h"
#include"timeit.h"
#include"var_subst.h"
#include"well_sorted.h"
#include "str_hashtable.h"
#include "front_end_params.h"
#include "z3_private.h"
#include "stopwatch.h"
#include "dl_rule.h"
// private method defined in z3.cpp:
front_end_params& Z3_API Z3_get_parameters(__in Z3_context c);
class id_param_info {
symbol m_string;
unsigned m_num_params;
parameter m_params[0];
public:
id_param_info(symbol const& s, unsigned n, parameter const* p) : m_string(s), m_num_params(n) {
for (unsigned i = 0; i < n; ++i) {
new (&(m_params[i])) parameter();
m_params[i] = p[i];
}
}
symbol string() const { return m_string; }
parameter * params() { return m_params; }
unsigned num_params() const { return m_num_params; }
};
class proto_region {
ptr_vector<rational> m_rationals;
ptr_vector< id_param_info > m_id_infos;
region m_region;
public:
proto_region() { }
~proto_region() { reset(); }
rational* allocate(rational const & n) {
rational* r = alloc(rational, n);
m_rationals.push_back(r);
return r;
}
id_param_info* allocate(vector<parameter> const& params, symbol const & s) {
unsigned size = sizeof(id_param_info) + sizeof(parameter)*(params.size());
id_param_info* r = static_cast<id_param_info*>(m_region.allocate(size));
new (r) id_param_info(s, params.size(), params.c_ptr());
m_id_infos.push_back(r);
return r;
}
void* allocate(size_t s) { return m_region.allocate(s); }
void reset() {
for (unsigned i = 0; i < m_rationals.size(); ++i) {
dealloc(m_rationals[i]);
}
for (unsigned i = 0; i < m_id_infos.size(); ++i) {
unsigned n = m_id_infos[i]->num_params();
for (unsigned j = 0; j < n; ++j) {
m_id_infos[i]->params()[j].~parameter();
}
}
m_rationals.reset();
m_id_infos.reset();
m_region.reset();
}
private:
};
inline void * operator new(size_t s, proto_region& r) { return r.allocate(s); }
inline void * operator new[](size_t s, proto_region & r) { return r.allocate(s); }
inline void operator delete(void*, proto_region& r) {}
inline void operator delete[](void *, proto_region& r) {}
class proto_expr {
public:
enum kind_t {
ID,
STRING,
COMMENT,
ANNOTATION,
INT,
FLOAT,
CONS
};
private:
int m_kind:8;
int m_line:24;
int m_pos;
union {
id_param_info* m_id_info;
rational* m_number;
proto_expr** m_children;
};
public:
symbol string() {
if (m_kind == INT || m_kind == FLOAT) {
std::string s = m_number->to_string();
return symbol(s.c_str());
}
if (m_kind == CONS) {
return symbol("");
}
SASSERT(m_kind == STRING || m_kind == COMMENT || m_kind == ID || m_kind == ANNOTATION);
return m_id_info->string();
}
rational const& number() {
SASSERT(m_kind == INT || m_kind == FLOAT);
return *m_number;
}
proto_expr* const* children() const {
if (m_kind == CONS) {
return m_children;
}
else {
return 0;
}
}
int line() { return m_line; }
int pos() { return m_pos; }
kind_t kind() { return static_cast<kind_t>(m_kind); }
unsigned num_params() const {
SASSERT(m_kind == ID);
return m_id_info->num_params();
}
parameter * params() {
SASSERT(m_kind == ID);
return m_id_info->params();
}
proto_expr(proto_region & region, kind_t kind, symbol const & s, vector<parameter> const & params, int line, int pos):
m_kind(kind),
m_line(line),
m_pos(pos),
m_id_info(region.allocate(params, s)) {
SASSERT(kind != CONS);
SASSERT(kind != INT);
SASSERT(kind != FLOAT);
}
proto_expr(proto_region& region, bool is_int, rational const & n, int line, int pos):
m_kind(is_int?INT:FLOAT),
m_line(line),
m_pos(pos),
m_number(region.allocate(n))
{}
proto_expr(proto_region& region, ptr_vector<proto_expr>& proto_exprs, int line, int pos):
m_kind(CONS),
m_line(line),
m_pos(pos) {
//
// null terminated list of proto_expression pointers.
//
unsigned num_children = proto_exprs.size();
m_children = new (region) proto_expr*[num_children+1];
for (unsigned i = 0; i < num_children; ++i) {
m_children[i] = proto_exprs[i];
}
m_children[num_children] = 0;
}
~proto_expr() {}
static proto_expr* copy(proto_region& r, proto_expr* e) {
switch(e->kind()) {
case proto_expr::CONS: {
ptr_vector<proto_expr> args;
proto_expr* const* children = e->children();
while (children && *children) {
args.push_back(copy(r, *children));
++children;
}
return new (r) proto_expr(r, args, e->line(), e->pos());
}
case proto_expr::INT: {
return new (r) proto_expr(r, true, e->number(), e->line(), e->pos());
}
case proto_expr::FLOAT: {
return new (r) proto_expr(r, false, e->number(), e->line(), e->pos());
}
case proto_expr::ID: {
vector<parameter> params;
for (unsigned i = 0; i < e->num_params(); ++i) {
params.push_back(e->params()[i]);
}
return new (r) proto_expr(r, e->kind(), e->string(), params, e->line(), e->pos());
}
default: {
vector<parameter> params;
return new (r) proto_expr(r, e->kind(), e->string(), params, e->line(), e->pos());
}
}
}
private:
proto_expr(proto_expr const & other);
proto_expr& operator=(proto_expr const & other);
};
//
// build up proto_expr tree from token stream.
//
class proto_expr_parser {
proto_region& m_region;
scanner& m_scanner;
std::ostream& m_err;
bool m_at_eof;
public:
proto_expr_parser(proto_region& region, scanner& scanner, std::ostream& err):
m_region(region),
m_scanner(scanner),
m_err(err),
m_at_eof(false) {
}
~proto_expr_parser() {}
bool parse(ptr_vector<proto_expr> & proto_exprs, bool parse_single_expr = false) {
scanner::token token;
vector<frame> stack;
proto_expr* result = 0;
stack.push_back(frame(PROTO_EXPRS_PRE));
token = m_scanner.scan();
if (token == scanner::EOF_TOKEN) {
proto_exprs.reset();
return true;
}
while (!stack.empty()) {
if (token == scanner::EOF_TOKEN) {
break;
}
if (token == scanner::ERROR_TOKEN) {
print_error("unexpected token");
goto done;
}
switch(stack.back().m_state) {
case PROTO_EXPR:
switch (token) {
case scanner::LEFT_PAREN:
stack.back().m_state = PROTO_EXPRS_PRE;
token = m_scanner.scan();
break;
default:
stack.back().m_state = ATOM;
break;
}
break;
case ATOM:
SASSERT(!result);
switch(token) {
case scanner::ID_TOKEN:
result = new (m_region) proto_expr(m_region, proto_expr::ID, m_scanner.get_id(), m_scanner.get_params(),
m_scanner.get_line(), m_scanner.get_pos());
break;
case scanner::INT_TOKEN:
result = new (m_region) proto_expr(m_region, true, m_scanner.get_number(), m_scanner.get_line(),
m_scanner.get_pos());
break;
case scanner::FLOAT_TOKEN:
result = new (m_region) proto_expr(m_region, false, m_scanner.get_number(), m_scanner.get_line(),
m_scanner.get_pos());
break;
case scanner::STRING_TOKEN:
result = new (m_region) proto_expr(m_region, proto_expr::STRING, m_scanner.get_id(), m_scanner.get_params(),
m_scanner.get_line(), m_scanner.get_pos());
break;
case scanner::COMMENT_TOKEN:
result = new (m_region) proto_expr(m_region, proto_expr::COMMENT, m_scanner.get_id(), m_scanner.get_params(),
m_scanner.get_line(), m_scanner.get_pos());
break;
case scanner::COLON:
token = m_scanner.scan();
if (token == scanner::ID_TOKEN) {
result = new (m_region) proto_expr(m_region, proto_expr::ANNOTATION, m_scanner.get_id(),
m_scanner.get_params(), m_scanner.get_line(), m_scanner.get_pos());
}
else {
print_error("unexpected identifier ':'");
token = scanner::ERROR_TOKEN;
goto done;
}
break;
default:
print_error("unexpected token");
token = scanner::ERROR_TOKEN;
goto done;
}
stack.pop_back();
SASSERT(!stack.empty());
stack.back().m_proto_exprs.push_back(result);
result = 0;
if (parse_single_expr && stack.size() == 1) {
goto done;
}
token = m_scanner.scan();
break;
case PROTO_EXPRS_PRE:
SASSERT(!result);
switch(token) {
case scanner::RIGHT_PAREN:
result = new (m_region) proto_expr(m_region, stack.back().m_proto_exprs, m_scanner.get_line(),
m_scanner.get_pos());
stack.pop_back();
if (stack.empty()) {
print_error("unexpected right parenthesis");
token = scanner::ERROR_TOKEN;
result = 0;
goto done;
}
stack.back().m_proto_exprs.push_back(result);
if (parse_single_expr && stack.size() == 1) {
goto done;
}
result = 0;
token = m_scanner.scan();
break;
case scanner::EOF_TOKEN:
m_at_eof = true;
break;
case scanner::ERROR_TOKEN:
print_error("could not parse expression");
goto done;
default:
stack.back().m_state = PROTO_EXPRS_POST;
stack.push_back(frame(PROTO_EXPR));
break;
}
break;
case PROTO_EXPRS_POST:
stack.back().m_state = PROTO_EXPRS_PRE;
break;
}
}
done:
if (stack.size() == 1) {
for (unsigned i = 0; i < stack.back().m_proto_exprs.size(); ++i) {
proto_exprs.push_back(stack.back().m_proto_exprs[i]);
}
return true;
}
if (stack.size() == 2) {
proto_exprs.push_back(new (m_region) proto_expr(m_region, stack.back().m_proto_exprs, m_scanner.get_line(),
m_scanner.get_pos()));
return true;
}
print_error("unexpected nesting of parenthesis: ", stack.size());
return false;
}
int get_line() {
return m_scanner.get_line();
}
bool at_eof() const {
return m_at_eof;
}
private:
template<typename T>
void print_error(char const* msg1, T msg2) {
m_err << "ERROR: line " << m_scanner.get_line()
<< " column " << m_scanner.get_pos() << ": "
<< msg1 << msg2 << "\n";
}
void print_error(char const* msg) {
print_error(msg, "");
}
// stack frame:
enum frame_state {
PROTO_EXPR,
PROTO_EXPRS_PRE,
PROTO_EXPRS_POST,
ATOM
};
class frame {
public:
frame_state m_state;
ptr_vector<proto_expr> m_proto_exprs;
frame(frame_state state):
m_state(state){
}
frame(frame const & other):
m_state(other.m_state),
m_proto_exprs(other.m_proto_exprs) {
}
private:
frame& operator=(frame const &);
};
};
enum smt_cmd_token {
SMT_CMD_SET_LOGIC, // logic-name
SMT_CMD_DECLARE_SORTS, // sorts-symbols
SMT_CMD_DECLARE_FUNS, // func-decls
SMT_CMD_DECLARE_PREDS, // pred-decls
SMT_CMD_DECLARE_DATATYPES, // datatypes
SMT_CMD_DEFINE, // var expr
SMT_CMD_DEFINE_SORTS, // (var sort)*
SMT_CMD_DECLARE_SORT, // <symbol> <numeral>
SMT_CMD_DEFINE_SORT, // <symbol> (<symbol>*) <sort>
SMT_CMD_DECLARE_FUN, // <symbol> (<sort>*) <sort>
SMT_CMD_DECLARE_CONST, // <symbol> <sort>
SMT_CMD_DEFINE_FUN, // <symbol> (<sorted_var>*) <sort> <term>
SMT_CMD_GET_VALUE, // (<term>+)
SMT_CMD_PUSH, // numeral
SMT_CMD_POP, // numeral
SMT_CMD_ASSERT, // expr
SMT_CMD_CHECK_SAT, //
SMT_CMD_GET_CORE, // expr+
SMT_CMD_NEXT_SAT,
SMT_CMD_SIMPLIFY, // expr
SMT_CMD_GET_IMPLIED_EQUALITIES, // expr*
SMT_CMD_EVAL, // expr
SMT_CMD_GET_ASSERTIONS, // string
SMT_CMD_GET_SAT_ASSERTIONS, // string
SMT_CMD_KEEP_SAT_ASSERTIONS, //
SMT_CMD_GET_UNSAT_CORE, // string
SMT_CMD_GET_PROOF, // string
SMT_CMD_SET_OPTION, // string strings
SMT_CMD_GET_INFO, // string
SMT_CMD_SET_INFO, // string strings
SMT_CMD_ECHO, // string strings
SMT_CMD_EXIT, //
// set-option:
SMT_CMD_PRINT_SUCCESS,
SMT_CMD_RANDOM_SEED,
SMT_CMD_VERBOSITY,
SMT_CMD_EXPAND_DEFINITIONS,
SMT_CMD_OUTPUT_CHANNEL,
SMT_CMD_VERBOSE_OUTPUT_CHANNEL,
SMT_CMD_ENABLE_CORES,
SMT_CMD_SET_PARAM,
SMT_CMD_PRODUCE_MODELS,
// set-info:
SMT_CMD_NAME,
SMT_CMD_VERSION,
SMT_CMD_AUTHORS,
SMT_CMD_LAST_FAILURE,
SMT_CMD_REASON_UNKNOWN,
SMT_CMD_STATS,
SMT_CMD_MODEL,
SMT_CMD_TIME,
SMT_CMD_LABELS,
SMT_CMD_HELP, // help
SMT_CMD_ERROR // error token
};
using namespace smtlib;
class idbuilder {
public:
virtual ~idbuilder() {}
virtual bool apply(expr_ref_vector const & args, expr_ref & result) = 0;
};
class builtin_sort_builder : public sort_builder {
ast_manager& m_manager;
family_id m_fid;
decl_kind m_kind;
public:
builtin_sort_builder(ast_manager& m, family_id fid, decl_kind k) :
m_manager(m), m_fid(fid), m_kind(k) {}
virtual bool apply(unsigned num_params, parameter const* params, sort_ref & result) {
result = m_manager.mk_sort(m_fid, m_kind, num_params, params);
return result.get() != 0;
}
};
class array_sort : public builtin_sort_builder {
public:
array_sort(ast_manager& m) :
builtin_sort_builder(m, m.get_family_id("array"), ARRAY_SORT) {}
};
class bv_sort : public builtin_sort_builder {
public:
bv_sort(ast_manager& m) :
builtin_sort_builder(m, m.get_family_id("bv"), BV_SORT) {}
};
class user_sort : public sort_builder {
user_sort_plugin * m_plugin;
decl_kind m_kind;
symbol m_name;
unsigned m_num_args;
std::string m_error_message;
public:
user_sort(ast_manager& m, unsigned num_args, symbol name):
m_name(name),
m_num_args(num_args) {
m_plugin = m.get_user_sort_plugin();
m_kind = m_plugin->register_name(name);
}
~user_sort() {}
virtual bool apply(unsigned num_params, parameter const* params, sort_ref & result) {
if (num_params != m_num_args) {
std::ostringstream strm;
strm << "wrong number of arguments passed to " << m_name << " "
<< m_num_args << " expected, but " << num_params << " given";
m_error_message = strm.str();
return false;
}
result = m_plugin->mk_sort(m_kind, num_params, params);
return true;
}
virtual char const* error_message() {
return m_error_message.c_str();
}
};
class scoped_stream {
std::ostream& m_default;
std::ostream* m_stream;
bool m_owned;
public:
scoped_stream(std::ostream& strm) : m_default(strm) {
m_stream = &strm;
m_owned = false;
}
scoped_stream(proto_expr* e) : m_default(std::cout), m_stream(0), m_owned(false) {
reset(e, 0);
}
scoped_stream(proto_expr* e, std::ostream& out) : m_default(out), m_stream(0), m_owned(false) {
reset(e, &out);
}
~scoped_stream() {
dealloc_stream();
}
void reset(proto_expr* e, std::ostream* out = 0) {
char const* name = (e && e->kind() == proto_expr::ID)?e->string().bare_str():0;
reset(name, out);
}
void reset(char const* name, std::ostream* out = 0) {
dealloc_stream();
m_owned = false;
if (!out) {
out = &m_default;
}
if (!name) {
m_stream = out;
}
else if (strcmp(name, "stdout")) {
m_stream = &std::cout;
}
else if (strcmp(name, "stderr")) {
m_stream = &std::cerr;
}
else {
m_stream = alloc(std::ofstream, name, std::ios_base::app);
m_owned = true;
if (m_stream->bad() || m_stream->fail()) {
dealloc(m_stream);
m_stream = out;
m_owned = false;
}
}
SASSERT(m_stream);
}
std::ostream& operator*() {
return *m_stream;
}
private:
void dealloc_stream() {
if (m_owned) {
m_stream->flush();
dealloc(m_stream);
}
m_stream = 0;
m_owned = false;
}
};
class cmd_exn {
Z3_error_code m_code;
public:
cmd_exn(Z3_error_code code) : m_code(code) {}
Z3_error_code get() const { return m_code; }
};
static void* g_sw = 0;
#define cmd_context _cmd_context
class cmd_context {
typedef map<char const *, smt_cmd_token, str_hash_proc, str_eq_proc> str2token;
str2token m_str2token;
ptr_vector<char const> m_token2str;
ptr_vector<char const> m_token2help;
ptr_vector<char const> m_token2args;
svector<bool> m_is_command;
struct cmd_info {
smt_cmd_token m_token;
void (*m_action)(cmd_context&);
cmd_info(smt_cmd_token t, void (*action)(cmd_context&)):
m_token(t), m_action(action) {}
};
struct opt {
smt_cmd_token m_token;
bool (*m_action)(cmd_context&, proto_expr* const* exprs);
opt(smt_cmd_token t, bool (*action)(cmd_context&, proto_expr* const* exprs)):
m_token(t), m_action(action) {}
};
enum check_sat_state {
state_unsat,
state_sat,
state_unknown,
state_clear,
state_new_assertion
};
Z3_context m_context;
Z3_model m_model;
Z3_ast m_proof;
ast_manager& m_manager;
scoped_stream m_out;
scoped_stream m_verbose;
symbol_table<idbuilder*> m_table;
region m_region;
ast_ref_vector m_trail;
unsigned_vector m_trail_lim;
expr_ref_vector m_asserted;
unsigned_vector m_asserted_lim;
expr_ref_vector m_asserted_proxies;
unsigned_vector m_asserted_proxies_lim;
bool m_print_success;
bool m_enable_cores;
unsigned m_core_size;
svector<Z3_ast> m_core;
check_sat_state m_check_sat_state;
svector<check_sat_state> m_check_sat_states;
stopwatch m_watch;
svector<cmd_info> m_infos;
svector<opt> m_options;
std::ostringstream m_error_stream;
smtlib::benchmark::status m_status;
public:
cmd_context(ast_manager& m, Z3_context ctx, std::istream& is, std::ostream& out):
m_context(ctx),
m_model(0),
m_proof(0),
m_manager(m),
m_out(out),
m_verbose(std::cerr),
m_trail(m),
m_asserted(m),
m_asserted_proxies(m),
m_print_success(Z3_get_parameters(ctx).m_smtlib2_compliant),
m_enable_cores(false),
m_core_size(0),
m_check_sat_state(state_clear),
m_status(smtlib::benchmark::UNKNOWN)
{
add_command("set-logic",
SMT_CMD_SET_LOGIC,
"<string>", "set the background logic");
add_command("declare-sorts",
SMT_CMD_DECLARE_SORTS,
"<sort declarations>", "declare sorts");
add_command("declare-funs",
SMT_CMD_DECLARE_FUNS,
"<function and constant declarations>",
"declare functions and constants");
add_command("declare-preds",
SMT_CMD_DECLARE_PREDS,
"<predicate declarations>",
"declare predicates");
add_command("declare-datatypes",
SMT_CMD_DECLARE_DATATYPES,
"<data type declarations>",
"declare recursive datatypes");
add_command("define",
SMT_CMD_DEFINE,
"<id> <expression> or (<id> (<id> <sort>)*) <expression>",
"define an expression shorthand");
add_command("define-sorts",
SMT_CMD_DEFINE_SORTS,
"(<id> <sort>)*",
"define shorthand for compound sorts, such as arrays");
add_command("declare-sort",
SMT_CMD_DECLARE_SORT,
"<sort declaration>", "declare sort");
add_command("define-sort",
SMT_CMD_DEFINE_SORT,
"<symbol> (<symbol>*) <sort>",
"define shorthand for compound sorts, such as arrays");
add_command("declare-fun",
SMT_CMD_DECLARE_FUN,
"<symbol> (<sort>*) <sort>",
"declare function or constant");
add_command("declare-const",
SMT_CMD_DECLARE_CONST,
"<symbol> <sort>",
"declare constant");
add_command("define-fun",
SMT_CMD_DEFINE_FUN,
"<symbol> (<sorted_var>*) <sort> <term>",
"define an expression shorthand");
add_command("get-value",
SMT_CMD_GET_VALUE,
"(<term>+)",
"evaluate list of terms");
add_command("push",
SMT_CMD_PUSH,
"[<number>]",
"push 1 (or <number>) scopes");
add_command("pop",
SMT_CMD_POP,
"[<number>]",
"pop 1 (or <number>) scopes");
add_command("assert",
SMT_CMD_ASSERT,
"<expression>",
"assert expression");
add_command("check-sat",
SMT_CMD_CHECK_SAT,
"<boolean-constants>*",
"check if the current context is satisfiable. If a list of boolean constants B is provided, then check if the current context is consistent with assigning every constant in B to true.");
add_command("get-core",
SMT_CMD_GET_CORE,
"<boolean-constants>+",
"check if the assumptions are consistent with the current context");
add_command("next-sat",
SMT_CMD_NEXT_SAT,
"",
"get the next satisfying assignment");
add_command("simplify",
SMT_CMD_SIMPLIFY,
"<expression>",
"simplify expression and print back result");
add_command("get-implied-equalities",
SMT_CMD_GET_IMPLIED_EQUALITIES,
"<expression>*",
"obtain list of identifiers for expressions, such that equal expressions have the same identfiers"
);
add_command("eval",
SMT_CMD_EVAL,
"<expression>",
"evaluate expression using the current model and print back result");
add_command("get-assertions",
SMT_CMD_GET_ASSERTIONS,
"[<file>]",
"retrieve current assertions");
add_command("get-sat-assertions",
SMT_CMD_GET_SAT_ASSERTIONS,
"[<file>]",
"retrieve current satisfying assignment");
add_command("keep-sat-assertions",
SMT_CMD_KEEP_SAT_ASSERTIONS,
"",
"assert current satisfying assignment");
add_command("get-unsat-core",
SMT_CMD_GET_UNSAT_CORE,
"[<file>]",
"retrieve unsatisfiable core of assertions");
add_command("get-proof",
SMT_CMD_GET_PROOF,
"[<file>]",
"retrieve proof");
add_command("set-option",
SMT_CMD_SET_OPTION,
"<strings>",
"set auxiliary options");
add_command("set-info",
SMT_CMD_SET_INFO,
"<attribute> <strings>",
"set auxiliary information");
add_command("get-info",
SMT_CMD_GET_INFO,
"<attribute>",
"retrieve auxiliary information");
add_command("echo",
SMT_CMD_ECHO,
"<strings>",
"display the given strings");
add_command("exit",
SMT_CMD_EXIT,
"",
"exit Z3 session");
m_str2token.insert("quit", SMT_CMD_EXIT);
add_option("print-success",
SMT_CMD_PRINT_SUCCESS,
"[<bool>]",
"toggle printing success",
&print_success_option);
add_option("verbosity",
SMT_CMD_VERBOSITY,
"<numeral>",
"set verbosity",
&verbosity_option);
add_option("regular-output-channel",
SMT_CMD_OUTPUT_CHANNEL,
"[<file>]",
"set name of alternate output channel",
&output_channel_option);
add_option("enable-cores",
SMT_CMD_ENABLE_CORES,
"",
"enable core extraction during solving",
&enable_cores_option);
add_option("set-param",
SMT_CMD_SET_PARAM,
"<param-id> <param-value>",
"update a mutable configuration parameter",
&update_param_option);
add_option("verbose-output-channel",
SMT_CMD_VERBOSE_OUTPUT_CHANNEL,
"<file>",
"set output channel",
&set_verbose_output_channel_option
);
add_option("produce-models",
SMT_CMD_PRODUCE_MODELS,
"[<bool>]",
"toggle model generation",
&produce_models_option);
//
// other options:
// add_option("random-seed", SMT_CMD_RANDOM_SEED, 0, "");
// add_option("expand-definitions",SMT_CMD_EXPAND_DEFINITIONS, 0, "");
// "produce-proofs"
// "produce-models"
// "produce-assignments"
//
add_info("name",
SMT_CMD_NAME,
"",
"solver name",
&name_info
);
add_info("version",
SMT_CMD_VERSION,
"",
"solver version",
&version_info
);
add_info("authors",
SMT_CMD_AUTHORS,
"",
"solver authors",
&authors_info);
add_info("statistics",
SMT_CMD_STATS,
"",
"search statistics",
&stats_info);
add_info("all-statistics",
SMT_CMD_STATS,
"",
"search statistics",
&stats_info);
add_info("model",
SMT_CMD_MODEL,
"",
"model from satisfied assertions",
&model_info
);
add_info("last-failure",
SMT_CMD_LAST_FAILURE,
"",
"reason for previous search failure",
&last_failure_info
);
add_info("reason-unknown",
SMT_CMD_REASON_UNKNOWN,
"",
"reason for previous unknown answer; 'memout' for out of memory, 'incomplete' for everything else",
&reason_unknown_info
);
add_info("time",
SMT_CMD_TIME,
"",
"time taken by solver",
&time_info
);
add_info("labels",
SMT_CMD_LABELS,
"",
"retrieve (Boogie) labels from satisfiable assertion",
&labels_info);
add_info("help",
SMT_CMD_HELP,
"",
"print this help",
&help_info);
#ifdef _EXTERNAL_RELEASE
Z3_set_ast_print_mode(m_context, Z3_PRINT_SMTLIB2_COMPLIANT);
#else
// use internal pretty printer
Z3_set_ast_print_mode(m_context, Z3_PRINT_SMTLIB_FULL);
// Z3_set_ast_print_mode(m_context, Z3_PRINT_SMTLIB2_COMPLIANT);
#endif
Z3_set_error_handler(m_context, error_handler);
set_error_stream(&m_error_stream);
set_warning_stream(&m_error_stream);
}
~cmd_context() {
if (m_model) {
Z3_del_model(m_context, m_model);
}
set_error_stream(0);
set_warning_stream(0);
}
//
// NB. As it is now, the symbol table used in m_benchmark is not
// scoped. Declarations just live on or get over-written.
//
void push(unsigned num_scopes) {
while (num_scopes > 0) {
m_table.begin_scope();
m_region.push_scope();
Z3_push(m_context);
m_trail_lim.push_back(m_trail.size());
m_asserted_lim.push_back(m_asserted.size());
m_asserted_proxies_lim.push_back(m_asserted_proxies.size());
--num_scopes;
m_check_sat_states.push_back(m_check_sat_state);
}
}
void pop(unsigned num_scopes) {
if (m_trail_lim.size() < num_scopes) {
num_scopes = m_trail_lim.size();
}
Z3_pop(m_context, num_scopes);
m_region.pop_scope(num_scopes);
while (num_scopes > 0) {
m_table.end_scope();
--num_scopes;
m_trail.resize(m_trail_lim.back());
m_trail_lim.pop_back();
m_asserted.resize(m_asserted_lim.back());
m_asserted_lim.pop_back();
m_asserted_proxies.resize(m_asserted_proxies_lim.back());
m_asserted_proxies_lim.pop_back();
m_check_sat_state = m_check_sat_states.back();
m_check_sat_states.pop_back();
}
m_proof = 0;
m_core_size = 0;
}
static void error_handler(Z3_context, Z3_error_code code) {
throw cmd_exn(code);
}
symbol_table<idbuilder*>& get_table() { return m_table; }
Z3_context get_context() { return m_context; }
std::ostream& get_out() { return *m_out; }
void print_quoted(const char* str) { get_out() << "\"" << str << "\""; }
void set_out(proto_expr* e) { m_out.reset(e); }
void add_trail(ast* a) { m_trail.push_back(a); }
void add_asserted(expr* e) {
if (get_enable_cores()) {
expr* proxy = m_manager.mk_fresh_const("proxy", m_manager.mk_bool_sort());
expr_ref imp(m_manager);
// It is not necessary to use iff (it is just overhead).
imp = m_manager.mk_implies(proxy, e);
TRACE("proxy", tout << "new proxy:\n " << mk_pp(imp, m_manager) << "\n";);
Z3_assert_cnstr(m_context, reinterpret_cast<Z3_ast>(imp.get()));
m_asserted_proxies.push_back(proxy);
}
else {
Z3_assert_cnstr(m_context, reinterpret_cast<Z3_ast>(e));
}
m_asserted.push_back(e);
if (m_check_sat_state != state_unsat) {
m_check_sat_state = state_new_assertion;
}
}
unsigned get_num_assertions() const { return m_asserted.size(); }
expr* get_assertion(unsigned idx) { return m_asserted[idx].get(); }
Z3_ast* get_proxies_ptr() { return reinterpret_cast<Z3_ast*>(m_asserted_proxies.c_ptr()); }
unsigned* get_core_size_ptr() { return &m_core_size; }
Z3_ast* get_core_ptr() {
m_core.resize(m_asserted_proxies.size());
if (m_core_size > m_core.size()) {
m_core_size = m_core.size();
}
return m_core.c_ptr();
}
region& get_region() { return m_region; }
void set_print_success(bool b) { m_print_success = b; }
void set_enable_cores() { m_enable_cores = true; }
void unset_enable_cores() { m_enable_cores = false; }
void update_param(char const* p, char const* v) {
Z3_update_param_value(m_context, p, v);
}
bool get_enable_cores() const { return m_enable_cores; }
void print_success() {
if (m_print_success) {
*m_out << "success\n";
}
}
void print_unsupported() {
*m_out << "unsupported\n";
}
void print_failure(char const* msg, proto_expr* expr) {
if (Z3_get_parameters(m_context).m_display_error_for_vs) {
if (msg[0] != 'Z' || msg[1] != '3') {
*m_out << "Z3";
if (expr) {
*m_out << "(" << expr->line() << "," << expr->pos() << ")";
}
*m_out << ": ERROR: ";
}
*m_out << msg;
if (msg[strlen(msg)-1] != '\n') {
*m_out << "\n";
}
}
else {
*m_out << "(error \"";
if (expr) {
*m_out << "line " << expr->line() << " column " << expr->pos() << ": ";
}
*m_out << escaped(msg, true) << "\")\n";
#ifndef _EXTERNAL_RELEASE
exit(ERR_PARSER);
#endif
}
}
Z3_model* get_model_ptr() {
if (m_model) {
Z3_del_model(m_context, m_model);
m_model = 0;
}
return &m_model;
}
Z3_model get_model() { return m_model; }
Z3_ast* get_proof_ptr() {
m_proof = 0;
return &m_proof;
}
void get_proof(std::ostream& out, proto_expr* p_expr) {
Z3_ast pr = m_proof;
if (pr) {
out << Z3_ast_to_string(m_context, pr) << "\n";
}
else if (Z3_get_parameters(m_context).m_proof_mode == PGM_DISABLED) {
print_failure("proofs are disabled - enable them using the configuration PROOF_MODE={1,2}", p_expr);
}
else {
print_failure("there is no proof to display", p_expr);
}
}
smt_cmd_token string2token(char const * str) {
str2token::entry * e = m_str2token.find_core(str);
if (e)
return e->get_data().m_value;
else
return SMT_CMD_ERROR;
}
class scoped_stopwatch {
cmd_context& m_ctx;
bool m_first;
void (*m_old_handler)(int);
static scoped_stopwatch* get_sw() { return static_cast<scoped_stopwatch*>(g_sw); }
static void on_ctrl_c(int) {
if (get_sw()->m_first) {
Z3_soft_check_cancel(get_sw()->m_ctx.get_context());
get_sw()->m_first = false;
}
else {
signal (SIGINT, get_sw()->m_old_handler);
raise (SIGINT);
}
}
public:
scoped_stopwatch(cmd_context& c) : m_ctx(c), m_first(true) {
g_sw = this;
c.m_watch.reset();
c.m_watch.start();
m_old_handler = signal(SIGINT, on_ctrl_c); // TBD: parallel?
}
~scoped_stopwatch() {
m_ctx.m_watch.stop();
if (m_old_handler != SIG_ERR) {
signal(SIGINT, m_old_handler);
}
}
};
//static scoped_stopwatch* g_sw = 0;
double get_seconds() {
return m_watch.get_seconds();
}
bool get_command_help(std::ostream& out, smt_cmd_token tok) {
if (!m_is_command[tok]) {
return false;
}
out << " (" << m_token2str[tok];
if (m_token2args[tok] && m_token2args[tok][0]) {
out << " " << m_token2args[tok];
}
out << ")\n";
out << " " << m_token2help[tok] << "\n\n";
return true;
}
void get_help(std::ostream& out) {
out << "\" available commands:\n\n";
for (unsigned i = 0; i < m_token2args.size(); ++i) {
get_command_help(out, static_cast<smt_cmd_token>(i));
}
get_info_help(out, " ");
out << "\n";
set_option_help(out, " ");
out << "\"";
}
bool get_info(smt_cmd_token tok) {
for (unsigned i = 0; i < m_infos.size(); ++i) {
if (m_infos[i].m_token == tok) {
get_out() << ":" << m_token2str[tok] << " ";
m_infos[i].m_action(*this);
return true;
}
}
return false;
}
void get_info_help(std::ostream& strm, char const* line_start) {
for (unsigned i = 0; i < m_infos.size(); ++i) {
smt_cmd_token tok = m_infos[i].m_token;
strm << line_start << "(get-info " << m_token2str[tok];
if (m_token2args[tok] && m_token2args[tok][0]) {
strm << " " << m_token2args[tok];
}
strm << ")\n";
strm << line_start << " " << m_token2help[tok] << "\n";
}
}
bool set_option(smt_cmd_token tok, proto_expr* const* chs) {
for (unsigned i = 0; i < m_options.size(); ++i) {
if (m_options[i].m_token == tok) {
return m_options[i].m_action(*this, chs);
}
}
return update_param_option(*this, chs-1);
// return false;
}
bool set_info(proto_expr* e0, proto_expr* const* chs) {
proto_expr* e1 = chs[0];
symbol s0, s1;
if (e0)
s0 = e0->string();
if (e1)
s1 = e1->string();
if (s0 == symbol("status") && s1 == symbol("sat")) {
m_status = smtlib::benchmark::SAT;
}
else if (s0 == symbol("status") && s1 == symbol("unsat")) {
m_status = smtlib::benchmark::UNSAT;
}
else if (s0 == symbol("status") && s1 == symbol("unknown")) {
m_status = smtlib::benchmark::UNKNOWN;
}
else {
#ifdef Z3DEBUG
std::cout << s0 << " " << s1 << "\n";
#endif
}
// :source
// :smt-lib-version
// :category
// :status
// no-op
return true;
}
void set_option_help(std::ostream& strm, char const* line_start) {
for (unsigned i = 0; i < m_options.size(); ++i) {
smt_cmd_token tok = m_options[i].m_token;
strm << line_start << "(set-option " << m_token2str[tok];
if (m_token2args[tok] && m_token2args[tok][0]) {
get_out() << " " << m_token2args[tok];
}
strm << ")\n";
strm << line_start << " " << m_token2help[tok] << "\n";
}
}
unsigned parse_opt_numeral(proto_expr* e, unsigned default_value) {
if (e && e->kind() == proto_expr::INT) {
rational r = e->number();
if (r.is_unsigned()) {
return r.get_unsigned();
}
}
return default_value;
}
bool parse_opt_bool(proto_expr* e, bool default_value) {
if (e && e->kind() == proto_expr::ID) {
if (strcmp(e->string().bare_str(), "true") == 0) {
return true;
}
if (strcmp(e->string().bare_str(), "false") == 0) {
return false;
}
}
return default_value;
}
void get_core(proto_expr* p_expr, std::ostream& out,
unsigned sz, expr** exprs, bool just_get_core) {
Z3_context ctx = get_context();
Z3_lbool r;
scoped_stopwatch stopw(*this);
if (get_enable_cores()) {
print_failure("cores should be disabled", p_expr);
return;
}
for (unsigned i = 0; i < sz; ++i) {
if (!is_uninterp_const(exprs[i]) || !m_manager.is_bool(exprs[i])) {
print_failure("assumptions must be boolean constants (e.g., p1, p2, q1)", p_expr);
return;
}
}
// set_enable_cores();
// push(1);
// for (unsigned i = 0; i < sz; ++i) {
// add_asserted(exprs[i]);
// }
unsigned max_core_size = sz;
unsigned core_size = sz;
m_core.reserve(max_core_size);
Z3_ast * core = m_core.c_ptr();
r = Z3_check_assumptions(ctx, sz, reinterpret_cast<Z3_ast*>(exprs),
get_model_ptr(), get_proof_ptr(),
&core_size, core);
switch(r) {
case Z3_L_FALSE:
if (!just_get_core)
out << "unsat\n";
print_core_as_is(out, core_size, core);
m_check_sat_state = state_unsat;
break;
case Z3_L_TRUE:
out << "sat\n";
m_check_sat_state = state_sat;
break;
case Z3_L_UNDEF:
out << "unknown\n";
m_check_sat_state = state_unknown;
break;
default:
throw default_exception("unexpected output of check-sat\n");
break;
}
// unset_enable_cores();
// pop(1);
}
void check_sat(std::ostream& out) {
Z3_context ctx = get_context();
Z3_lbool r;
scoped_stopwatch stopw(*this);
if (get_enable_cores()) {
r = Z3_check_assumptions(ctx, get_num_assertions(), get_proxies_ptr(),
get_model_ptr(), get_proof_ptr(),
get_core_size_ptr(), get_core_ptr());
}
else if (Z3_get_parameters(ctx).m_proof_mode != PGM_DISABLED) {
unsigned core_size = 0;
Z3_ast core[1] = { 0 };
r = Z3_check_assumptions(ctx, 0, 0, get_model_ptr(), get_proof_ptr(), &core_size, core);
}
else if (Z3_get_parameters(ctx).m_model) {
r = Z3_check_and_get_model(ctx, get_model_ptr());
}
else {
r = Z3_check(ctx);
}
switch(r) {
case Z3_L_FALSE:
out << "unsat\n";
m_check_sat_state = state_unsat;
break;
case Z3_L_TRUE:
out << "sat\n";
m_check_sat_state = state_sat;
break;
case Z3_L_UNDEF:
out << "unknown\n";
m_check_sat_state = state_unknown;
break;
default:
throw default_exception("unexpected output of check-sat\n");
break;
}
// check status (duplicate from smtlib_sover)
// smtlib_solver contains support for
// - spc
// - missing.
// - dumping statistics / proofs / model / labels
// - redundant given additional command-line options
//
switch(m_status) {
case smtlib::benchmark::SAT:
if (r == Z3_L_FALSE) {
#ifdef _EXTERNAL_RELEASE
std::cout << "unsat - check annotation which says sat\n";
#else
std::cout << "BUG: unsoundness.\n";
exit(ERR_UNSOUNDNESS);
#endif
}
else if (r == Z3_L_UNDEF) {
#ifndef _EXTERNAL_RELEASE
std::cout << "BUG: gaveup.\n";
exit(ERR_UNKNOWN_RESULT);
#endif
}
break;
case smtlib::benchmark::UNSAT:
if (r == Z3_L_TRUE) {
#ifdef _EXTERNAL_RELEASE
std::cout << "sat - check annotation which says unsat\n";
#else
std::cout << "BUG: incompleteness.\n";
exit(ERR_INCOMPLETENESS);
#endif
}
else if (r == Z3_L_UNDEF) {
#ifndef _EXTERNAL_RELEASE
std::cout << "BUG: gaveup.\n";
exit(ERR_UNKNOWN_RESULT);
#endif
}
break;
default:
break;
}
}
void next_sat(std::ostream& out) {
Z3_context ctx = get_context();
if (m_check_sat_state == state_sat || m_check_sat_state == state_unknown) {
Z3_literals lits = Z3_get_relevant_literals(ctx);
if (lits) {
Z3_block_literals(ctx, lits);
Z3_del_literals(ctx, lits);
}
}
check_sat(out);
}
void get_sat_assertions(std::ostream& out) {
if (m_check_sat_state == state_unsat) {
out << "false\n";
}
else {
Z3_ast assignment = Z3_get_context_assignment(m_context);
out << Z3_ast_to_string(m_context, reinterpret_cast<Z3_ast>(assignment)) << "\n";
}
}
void get_implied_equalities(std::ostream& out, unsigned num_exprs, expr* const* exprs) {
buffer<unsigned> class_ids(num_exprs, UINT_MAX);
Z3_lbool r = Z3_get_implied_equalities(m_context,
num_exprs,
(Z3_ast*) exprs,
class_ids.c_ptr());
if (r == Z3_L_FALSE) {
out << "unsat\n";
return;
}
out << "(";
for (unsigned i = 0; i < num_exprs; ++i) {
out << class_ids[i];
if (i + 1 < num_exprs) {
out << " ";
}
}
out << ")\n";
}
void eval(std::ostream& out, proto_expr* p_expr, expr* e) {
Z3_model m = get_model();
if (!m) {
print_failure("There is no model in the current context", p_expr);
return;
}
Z3_ast result = 0;
Z3_bool fully_simplified = Z3_eval(m_context, m, reinterpret_cast<Z3_ast>(e), &result);
if (!result) {
print_failure("Evaluation was not successful", p_expr);
return;
}
(void) fully_simplified;
out << Z3_ast_to_string(m_context, result) << "\n";
}
void eval(std::ostream& out, proto_expr* p_expr, unsigned num_args, expr*const* args) {
svector<Z3_ast> results;
Z3_model m = get_model();
if (!m) {
print_failure("There is no model in the current context", p_expr);
return;
}
for (unsigned i = 0; i < num_args; ++i) {
Z3_ast result = 0;
Z3_bool fully_simplified = Z3_eval(m_context, m, reinterpret_cast<Z3_ast>(args[i]), &result);
if (!result) {
print_failure("Evaluation was not successful", p_expr);
return;
}
(void) fully_simplified;
results.push_back(result);
}
out << "(";
for (unsigned i = 0; i < num_args; ++i) {
out << Z3_ast_to_string(m_context, results[i]);
if (i + 1 < num_args) {
out << "\n";
}
}
out << ")\n";
}
void get_unsat_core(std::ostream& out, proto_expr* p_expr) {
if (!get_enable_cores()) {
print_failure("cores have not been enabled, use (set-option enable-cores)", p_expr);
return;
}
print_core(out);
}
Z3_ast find_proxy(Z3_ast proxy) {
for (unsigned i = 0; i < m_asserted.size(); ++i) {
if (m_asserted_proxies[i] == (expr*)proxy) {
return reinterpret_cast<Z3_ast>(m_asserted[i].get());
}
}
UNREACHABLE();
return proxy;
}
void print_core(std::ostream& out) {
unsigned csz = *get_core_size_ptr();
out << "(";
for (unsigned i = 0; i < csz; ++i) {
out << Z3_ast_to_string(m_context, find_proxy(get_core_ptr()[i]));
if (i + 1 < csz) out << "\n";
}
out << ")\n";
}
void print_core_as_is(std::ostream & out, unsigned csz, Z3_ast * core) {
out << "(";
for (unsigned i = 0; i < csz; ++i) {
out << Z3_ast_to_string(m_context, core[i]);
if (i + 1 < csz) out << " ";
}
out << ")\n";
}
void get_assertions(std::ostream& out) {
out << "(";
unsigned num_assertions = get_num_assertions();
for (unsigned i = 0; i < num_assertions; ++i) {
out << Z3_ast_to_string(m_context, reinterpret_cast<Z3_ast>(get_assertion(i)));
if (i + 1 < num_assertions) out << "\n";
}
out << ")\n";
}
bool has_error() {
return m_error_stream.tellp() > 0;
}
void flush_errors() {
if (has_error()) {
m_error_stream.put(0);
print_failure(m_error_stream.str().c_str(), 0);
m_error_stream.seekp(0);
m_error_stream.clear();
}
}
std::ostream& get_error_stream() {
return m_error_stream;
}
private:
void add_command(
char const* name,
smt_cmd_token tok,
char const* args,
char const* help,
bool is_command = true
) {
m_str2token.insert(name, tok);
if ((unsigned)tok >= m_token2str.size()) {
m_token2str.resize(tok+1);
m_token2help.resize(tok+1);
m_token2args.resize(tok+1);
m_is_command.resize(tok+1);
}
m_token2str[tok] = name;
m_token2help[tok] = help;
m_token2args[tok] = args;
m_is_command[tok] = is_command;
}
void add_info(
char const* name,
smt_cmd_token t,
char const* args,
char const* help,
void (*action)(cmd_context&)
)
{
add_command(name, t, args, help, false);
m_infos.push_back(cmd_info(t, action));
}
void add_option(
char const* name,
smt_cmd_token t,
char const* args,
char const* help,
bool (*action)(cmd_context&, proto_expr* const* exprs)
)
{
add_command(name, t, args, help, false);
m_options.push_back(opt(t, action));
}
static void name_info(cmd_context& ctx) { ctx.print_quoted("Z3"); }
static void version_info(cmd_context& ctx) {
unsigned maj, min, bn, rn;
Z3_get_version(&maj,&min,&bn,&rn);
ctx.get_out() << "\"" << maj << "." << min << "-" << bn << "." << rn << "\"";
}
static void authors_info(cmd_context& ctx) { ctx.print_quoted("Leonardo de Moura and Nikolaj Bjorner"); }
static void last_failure_info(cmd_context& cmd_ctx) {
Z3_context ctx = cmd_ctx.get_context();
Z3_search_failure sf = Z3_get_search_failure(ctx);
static char const * reasons[8] = { "no failure", "unknown", "timeout", "memout",
"user canceled", "exceeded conflict bound",
"incomplete theory support",
"formulas used quantifiers that may not have been instantiated fully"
};
if (sf < 8) {
cmd_ctx.print_quoted(reasons[sf]);
}
else {
cmd_ctx.print_quoted("not documented");
UNREACHABLE();
}
}
static void reason_unknown_info(cmd_context& cmd_ctx) {
Z3_context ctx = cmd_ctx.get_context();
Z3_search_failure sf = Z3_get_search_failure(ctx);
if (sf == 3)
cmd_ctx.print_quoted("memout");
else
cmd_ctx.print_quoted("incomplete");
}
static void stats_info(cmd_context& cmd_ctx) {
cmd_ctx.get_out() << "\"" << escaped(Z3_statistics_to_string(cmd_ctx.get_context()), true) << "\"";
}
static void model_info(cmd_context& cmd_ctx) {
Z3_context ctx = cmd_ctx.get_context();
Z3_model m = cmd_ctx.get_model();
if (m) {
if (Z3_get_parameters(ctx).m_model_v1_pp || Z3_get_parameters(ctx).m_model_v2_pp) {
cmd_ctx.get_out() << "\"" << escaped(Z3_model_to_string(ctx, m), true) << "\"";
} else {
cmd_ctx.get_out() << "(z3_model" << std::endl
<< Z3_model_to_string(ctx, m)
<< ")";
}
}
else if (!Z3_get_parameters(ctx).m_model) {
cmd_ctx.print_quoted("models are disabled - enable them using the configuration MODEL=true");
}
else {
cmd_ctx.print_quoted("there is no model at the current scope");
}
}
static void time_info(cmd_context& cmd_ctx) {
cmd_ctx.get_out() << cmd_ctx.get_seconds();
}
static void labels_info(cmd_context& cmd_ctx) {
std::ostream& out = cmd_ctx.get_out();
Z3_context ctx = cmd_ctx.get_context();
Z3_literals lits = Z3_get_relevant_labels(ctx);
unsigned sz = Z3_get_num_literals(ctx, lits);
if (sz > 0) {
out << "(z3_labels";
for (unsigned i = 0; i < sz; ++i) {
out << " ";
out << Z3_get_symbol_string(ctx, Z3_get_label_symbol(ctx, lits, i));
}
out << ")";
}
Z3_del_literals(ctx, lits);
}
static void help_info(cmd_context& cmd_ctx) {
cmd_ctx.get_help(cmd_ctx.get_out());
}
static bool print_success_option(cmd_context& cmd_ctx, proto_expr*const* chs) {
cmd_ctx.set_print_success(cmd_ctx.parse_opt_bool(chs?*chs:0, true));
return true;
}
static bool produce_models_option(cmd_context& cmd_ctx, proto_expr*const* chs) {
cmd_ctx.update_param("MODEL", cmd_ctx.parse_opt_bool(chs?*chs:0, true) ? "true" : "false");
return true;
}
static bool verbosity_option(cmd_context& cmd_ctx, proto_expr*const* chs) {
unsigned lvl = cmd_ctx.parse_opt_numeral(chs?*chs:0, 0);
set_verbosity_level(lvl);
return true;
}
static bool output_channel_option(cmd_context& cmd_ctx, proto_expr*const* chs) {
cmd_ctx.set_out(chs?*chs:0);
return true;
}
static bool enable_cores_option(cmd_context& cmd_ctx, proto_expr*const* chs) {
cmd_ctx.set_enable_cores();
return true;
}
static void print_parameters(cmd_context& cmd_ctx) {
front_end_params& p = Z3_get_parameters(cmd_ctx.m_context);
ini_params ini;
p.register_params(ini);
ini.display_params(cmd_ctx.get_out());
}
static void print_parameter_help(char const* param, cmd_context& cmd_ctx) {
front_end_params& p = Z3_get_parameters(cmd_ctx.m_context);
ini_params ini;
p.register_params(ini);
ini.display_parameter_help(param,cmd_ctx.get_out());
}
static bool update_param_option(cmd_context& cmd_ctx, proto_expr*const* chs) {
if (!chs) {
print_parameters(cmd_ctx);
return false;
}
if (chs[0] && !chs[1] && chs[0]->kind() == proto_expr::CONS) {
chs = chs[0]->children();
}
proto_expr* p = chs[0];
proto_expr* v = chs[1];
char const* p_string = 0;
char const*v_string = 0;
std::string v_str;
if (!p || (p->kind() != proto_expr::ID && p->kind() != proto_expr::STRING && p->kind() != proto_expr::ANNOTATION)) {
print_parameters(cmd_ctx);
return false;
}
p_string = p->string().bare_str();
if (v && (v->kind() == proto_expr::INT || v->kind() == proto_expr::FLOAT)) {
v_str += v->number().to_string();
v_string = v_str.c_str();
}
else if (!v || (v->kind() != proto_expr::ID && v->kind() != proto_expr::STRING)) {
print_parameter_help(p->string().bare_str(), cmd_ctx);
return false;
}
else {
v_str = v->string().bare_str();
if (v_str.length() > 2 && v_str[0] == '|' && v_str[v_str.length() - 1] == '|') {
// strip the quotes
v_str = v_str.substr(1, v_str.length() - 2);
}
v_string = v_str.c_str();
}
// hack for generating warning message when trying to set PROOF_MODE inside the command context.
if (strcmp(p_string, "PROOF_MODE") == 0) {
warning_msg("PROOF_MODE can only be set as a command line option when invoking z3.exe (e.g., \"z3.exe PROOF_MODE=2 file.smt2\"), or when creating a fresh logical context using the Z3 API.");
return false;
}
cmd_ctx.update_param(p_string, v_string);
return true;
}
static bool set_verbose_output_channel_option(cmd_context& cmd_ctx, proto_expr*const* chs) {
cmd_ctx.m_verbose.reset(chs?(*chs):0);
set_verbose_stream(*cmd_ctx.m_verbose);
return true;
}
};
class smtparser : public parser {
struct builtin_op {
family_id m_family_id;
decl_kind m_kind;
builtin_op() : m_family_id(null_family_id), m_kind(0) {}
builtin_op(family_id fid, decl_kind k) : m_family_id(fid), m_kind(k) {}
};
class add_plugins {
public:
add_plugins(ast_manager& m) {
#define REGISTER_PLUGIN(NAME, MK) { \
family_id fid = m.get_family_id(symbol(NAME)); \
if (!m.has_plugin(fid)) { \
m.register_plugin(fid, MK); \
} \
} ((void) 0)
REGISTER_PLUGIN("arith", alloc(arith_decl_plugin));
REGISTER_PLUGIN("bv", alloc(bv_decl_plugin));
REGISTER_PLUGIN("array", alloc(array_decl_plugin));
};
};
ast_manager& m_manager;
add_plugins m_plugins;
arith_util m_anum_util;
bv_util m_bvnum_util;
pattern_validator m_pattern_validator;
bool m_ignore_user_patterns;
unsigned m_binding_level; // scope level for bound vars
benchmark m_benchmark; // currently parsed benchmark
sort_ref_vector m_pinned_sorts;
typedef map<symbol, builtin_op, symbol_hash_proc, symbol_eq_proc> op_map;
op_map m_builtin_ops;
op_map m_builtin_sorts;
symbol m_let; // commonly used symbols.
symbol m_flet;
symbol m_forall;
symbol m_exists;
symbol m_lblneg;
symbol m_lblpos;
symbol m_associative;
symbol m_commutative;
symbol m_injective;
symbol m_sorts;
symbol m_funs;
symbol m_preds;
symbol m_definition;
symbol m_axioms;
symbol m_notes;
symbol m_theory;
symbol m_language;
symbol m_extensions;
symbol m_array;
symbol m_bang;
symbol m_underscore;
sort* m_int_sort;
sort* m_real_sort;
family_id m_bv_fid;
family_id m_arith_fid;
family_id m_array_fid;
family_id m_rel_fid;
datatype_decl_plugin * m_dt_plugin;
datatype_util m_dt_util;
substitution_tree m_st;
func_decl * m_sk_hack;
std::ostream* m_err;
bool m_display_error_for_vs;
public:
smtparser(ast_manager& m, bool ignore_user_patterns):
m_manager(m),
m_plugins(m),
m_anum_util(m),
m_bvnum_util(m),
m_pattern_validator(m),
m_ignore_user_patterns(ignore_user_patterns),
m_binding_level(0),
m_benchmark(m_manager, symbol("")),
m_pinned_sorts(m),
m_let("let"),
m_flet("flet"),
m_forall("forall"),
m_exists("exists"),
m_lblneg("lblneg"),
m_lblpos("lblpos"),
m_associative("assoc"),
m_commutative("comm"),
m_injective("injective"),
m_sorts("sorts"),
m_funs("funs"),
m_preds("preds"),
m_definition("definition"),
m_axioms("axioms"),
m_notes("notes"),
m_theory("theory"),
m_language("language"),
m_extensions("extensions"),
m_array("array"),
m_bang("!"),
m_underscore("_"),
m_dt_plugin(0),
m_dt_util(m),
m_st(m),
m_err(0),
m_display_error_for_vs(false)
{
family_id bfid = m_manager.get_basic_family_id();
add_builtin_type("bool", bfid, BOOL_SORT);
m_benchmark.get_symtable()->insert(symbol("Array"), alloc(array_sort, m));
m_benchmark.get_symtable()->insert(symbol("BitVec"), alloc(bv_sort, m));
add_builtins(bfid);
}
~smtparser() {
}
void set_error_stream(std::ostream& strm) { m_err = &strm; }
std::ostream& get_err() { return m_err?(*m_err):std::cerr; }
bool ignore_user_patterns() const { return m_ignore_user_patterns; }
bool parse_stream(std::istream& stream) {
proto_region region;
scanner scanner(stream, get_err(), false);
proto_expr_parser parser(region, scanner, get_err());
return parse(parser);
}
bool parse_file(char const * filename) {
timeit tt(get_verbosity_level() >= 100, "parsing file");
if (filename != 0) {
std::ifstream stream(filename);
if (!stream) {
get_err() << "ERROR: could not open file '" << filename << "'.\n";
return false;
}
return parse_stream(stream);
}
else {
return parse_stream(std::cin);
}
}
bool parse_string(char const * str) {
std::string s = str;
std::istringstream is(s);
return parse_stream(is);
}
bool parse_commands(Z3_context ctx, std::istream& is, std::ostream& os) {
set_error_stream(os);
cmd_context context(m_manager, ctx, is, os);
set_error_stream(context.get_error_stream());
proto_region proto_region;
scanner scanner(is, context.get_error_stream(), true);
proto_expr_parser parser(proto_region, scanner, context.get_error_stream());
m_display_error_for_vs = Z3_get_parameters(ctx).m_display_error_for_vs;
ptr_vector<proto_expr> exprs;
while (!parser.at_eof()) {
proto_region.reset();
exprs.reset();
if (!parser.parse(exprs, true)) {
context.flush_errors();
context.get_out().flush();
break;
}
if (exprs.empty()) {
break;
}
SASSERT(exprs.size() == 1);
try {
if (!process_command(context, exprs.back())) {
break;
}
}
catch(cmd_exn(ex)) {
context.flush_errors();
context.get_out().flush();
context.print_failure(Z3_get_error_msg(ex.get()), exprs.back());
}
context.flush_errors();
context.get_out().flush();
}
return true;
}
void add_builtin_op(char const * s, family_id fid, decl_kind k) {
m_builtin_ops.insert(symbol(s), builtin_op(fid, k));
}
void add_builtin_type(char const * s, family_id fid, decl_kind k) {
m_builtin_sorts.insert(symbol(s), builtin_op(fid, k));
}
void initialize_smtlib() {
if (!m_dt_plugin) {
family_id fid = m_manager.get_family_id("datatype");
if (!m_manager.has_plugin(fid)) {
m_manager.register_plugin(fid, alloc(datatype_decl_plugin));
}
m_dt_plugin = static_cast<datatype_decl_plugin*>(m_manager.get_plugin(fid));
}
smtlib::symtable* table = m_benchmark.get_symtable();
symbol arith("arith");
family_id afid = m_manager.get_family_id(arith);
m_arith_fid = afid;
add_builtin_type("Int", afid, INT_SORT);
add_builtin_type("Real", afid, REAL_SORT);
add_builtin_type("Bool", m_manager.get_basic_family_id(), BOOL_SORT);
m_int_sort = m_manager.mk_sort(m_arith_fid, INT_SORT);
m_real_sort = m_manager.mk_sort(m_arith_fid, REAL_SORT);
add_builtins(afid);
symbol bv("bv");
family_id bfid = m_manager.get_family_id(bv);
m_bv_fid = bfid;
add_builtins(bfid);
add_builtin_type("BitVec", bfid, BV_SORT);
symbol array("array");
afid = m_manager.get_family_id(array);
m_array_fid = afid;
add_builtins(afid);
sort* a1, *a2;
func_decl* store1, *sel1, *store2, *sel2;
// Array
parameter params0[2] = { parameter(m_int_sort), parameter(m_int_sort) };
a1 = m_manager.mk_sort(afid, ARRAY_SORT, 2, params0);
table->insert(symbol("Array"), a1);
parameter param0(a1);
sort * args0[3] = { a1, m_int_sort, m_int_sort };
store1 = m_manager.mk_func_decl(afid, OP_STORE, 0, 0, 3, args0);
table->insert(symbol("store"), store1);
sel1 = m_manager.mk_func_decl(afid, OP_SELECT, 0, 0, 2, args0);
table->insert(symbol("select"), sel1);
// Array1
parameter params1[2] = { parameter(m_int_sort), parameter(m_real_sort) };
a1 = m_manager.mk_sort(afid, ARRAY_SORT, 2, params1);
table->insert(symbol("Array1"), a1);
parameter param1(a1);
sort * args1[3] = { a1, m_int_sort, m_real_sort };
store1 = m_manager.mk_func_decl(afid, OP_STORE, 0, 0, 3, args1);
table->insert(symbol("store"), store1);
sel1 = m_manager.mk_func_decl(afid, OP_SELECT, 0, 0, 2, args1);
table->insert(symbol("select"), sel1);
// Array2
parameter params2[2] = { parameter(m_int_sort), parameter(a1) };
a2 = m_manager.mk_sort(afid, ARRAY_SORT, 2, params2);
table->insert(symbol("Array2"), a2);
parameter param2(a2);
sort * args2[3] = { a2, m_int_sort, a1 };
store2 = m_manager.mk_func_decl(afid, OP_STORE, 0, 0, 3, args2);
table->insert(symbol("store"), store2);
sel2 = m_manager.mk_func_decl(afid, OP_SELECT, 0, 0, 2, args2);
table->insert(symbol("select"), sel2);
m_benchmark.declare_sort(symbol("U"));
sort * bool_sort = m_manager.mk_bool_sort();
m_sk_hack = m_manager.mk_func_decl(symbol("sk_hack"), 1, &bool_sort, bool_sort);
table->insert(symbol("sk_hack"), m_sk_hack);
}
void add_builtins(family_id fid) {
decl_plugin * plugin = m_manager.get_plugin(fid);
SASSERT(plugin);
svector<builtin_name> op_names;
plugin->get_op_names(op_names);
for (unsigned i = 0; i < op_names.size(); ++i) {
add_builtin_op(op_names[i].m_name.bare_str(), fid, op_names[i].m_kind);
}
}
smtlib::benchmark* get_benchmark() { return &m_benchmark; }
private:
bool parse(proto_expr_parser& parser) {
symbol benchmark("benchmark");
symbol name("");
proto_expr* const* rest = 0;
ptr_vector<proto_expr> proto_exprs;
bool result = parser.parse(proto_exprs);
proto_expr* proto_expr = 0;
if (!result) {
get_err() << "ERROR: parse error at line " << parser.get_line() << ".\n";
}
for (unsigned i = 0; result && i < proto_exprs.size(); ++i) {
proto_expr = proto_exprs[i];
if (match_cons(proto_expr, benchmark, name, rest)) {
result = make_benchmark(name, rest);
}
else if (proto_expr && proto_expr->kind() != proto_expr::COMMENT) {
set_error("could not match expression to benchmark ", proto_expr);
}
else {
// proto_expr->kind() == proto_expr::COMMENT.
}
}
return result;
}
void error_prefix(proto_expr* e) {
if (m_display_error_for_vs) {
if (e) {
get_err() << "Z3(" << e->line() << "," << e->pos() << "): ERROR: ";
}
}
else {
get_err() << "ERROR: ";
if (e) {
get_err() << "line " << e->line() << " column " << e->pos() << ": ";
}
}
}
void set_error(char const * str, proto_expr* e) {
error_prefix(e);
if (e->kind() == proto_expr::ID) {
get_err() << str << " '" << e->string() << "'.\n";
}
else {
get_err() << str << ".\n";
}
}
template<typename T1, typename T2>
void set_error(T1 str1, T2 str2, proto_expr* e) {
error_prefix(e);
get_err() << str1 << " " << str2 << ".\n";
}
template<typename T1, typename T2, typename T3>
void set_error(T1 str1, T2 str2, T3 str3, proto_expr* e) {
error_prefix(e);
get_err() << str1 << str2 << str3 << ".\n";
}
bool match_cons(proto_expr * e, symbol const & sym, symbol & name, proto_expr* const*& proto_exprs) {
if (e &&
e->kind() == proto_expr::CONS &&
e->children() &&
e->children()[0] &&
e->children()[0]->string() == sym &&
e->children()[1]) {
proto_exprs = e->children() + 2;
name = e->children()[1]->string();
return true;
}
return false;
}
bool make_benchmark(symbol & name, proto_expr * const* proto_exprs) {
symbol extrasorts("extrasorts");
symbol extrafuns("extrafuns");
symbol extrapreds("extrapreds");
symbol datatypes("datatypes");
symbol unify("unify");
symbol unify_fail("unify-fail");
#if !defined(SMTCOMP) && !defined(_EXTERNAL_RELEASE)
symbol kbo_lt("kbo_lt");
symbol kbo_gt("kbo_gt");
symbol kbo_eq("kbo_eq");
symbol kbo_un("kbo_un");
symbol lpo_lt("lpo_lt");
symbol lpo_gt("lpo_gt");
symbol lpo_eq("lpo_eq");
symbol lpo_un("lpo_un");
symbol st_insert("st_insert");
symbol st_erase("st_erase");
symbol st_reset("st_reset");
symbol st_unify("st_unify");
symbol st_inst("st_inst");
symbol st_gen("st_gen");
symbol st_display("st_display");
#endif
symbol assumption("assumption");
symbol assumption_core("assumption-core");
symbol define_sorts_sym("define_sorts");
symbol logic("logic");
symbol formula("formula");
symbol status("status");
symbol sat("sat");
symbol unsat("unsat");
symbol unknown("unknown");
symbol empty("");
symbol source("source");
symbol difficulty("difficulty");
symbol category("category");
bool success = true;
push_benchmark(name);
while (proto_exprs && *proto_exprs) {
proto_expr* e = *proto_exprs;
++proto_exprs;
proto_expr* e1 = *proto_exprs;
if (logic == e->string() && e1) {
name = e1->string();
m_benchmark.set_logic(name);
set_default_num_sort(name);
if (name == symbol("QF_AX")) {
// Hack for supporting new QF_AX theory...
sort * index = m_manager.mk_sort(symbol("Index"));
sort * element = m_manager.mk_sort(symbol("Element"));
parameter params[2] = { parameter(index), parameter(element) };
sort * array = m_manager.mk_sort(m_array_fid, ARRAY_SORT, 2, params);
smtlib::symtable* table = m_benchmark.get_symtable();
table->insert(symbol("Index"), index);
table->insert(symbol("Element"), element);
// overwrite Array definition...
table->insert(symbol("Array"), array);
sort * args[3] = { array, index, element };
func_decl * store = m_manager.mk_func_decl(m_array_fid, OP_STORE, 0, 0, 3, args);
table->insert(symbol("store"), store);
func_decl * sel = m_manager.mk_func_decl(m_array_fid, OP_SELECT, 0, 0, 2, args);
table->insert(symbol("select"), sel);
}
++proto_exprs;
continue;
}
if (assumption == e->string() && e1) {
expr_ref t(m_manager);
if (!make_expression(e1, t) ||
!push_assumption(t.get())) {
return false;
}
++proto_exprs;
continue;
}
if (assumption_core == e->string() && e1) {
expr_ref t(m_manager);
if (!make_expression(e1, t))
return false;
m_benchmark.add_assumption(t.get());
++proto_exprs;
continue;
}
if (define_sorts_sym == e->string() && e1) {
if (!define_sorts(e1)) {
return false;
}
++proto_exprs;
continue;
}
if (formula == e->string() && e1) {
expr_ref t(m_manager);
if (!make_expression(e1, t) ||
!push_formula(t.get())) {
return false;
}
++proto_exprs;
continue;
}
if (status == e->string() && e1) {
if (sat == e1->string()) {
if (!push_status(smtlib::benchmark::SAT)) {
set_error("could not push status ", e1->string(),e1);
return false;
}
}
else if (unsat == e1->string()) {
if (!push_status(smtlib::benchmark::UNSAT)) {
set_error("could not push status ", e1->string(),e1);
return false;
}
}
else if (unknown == e1->string()) {
if (!push_status(smtlib::benchmark::UNKNOWN)) {
set_error("could not push status ", e1->string(),e1);
return false;
}
}
else {
set_error("could not recognize status ", e1->string(),e1);
return false;
}
++proto_exprs;
continue;
}
if (extrasorts == e->string() && e1) {
if (!declare_sorts(e1)) {
return false;
}
++proto_exprs;
continue;
}
if (extrafuns == e->string() && e1) {
if (!declare_funs(e1)) {
return false;
}
++proto_exprs;
continue;
}
if (extrapreds == e->string() && e1) {
if (!declare_preds(e1)) {
return false;
}
++proto_exprs;
continue;
}
if (datatypes == e->string() && e1) {
if (!declare_datatypes(e1)) {
return false;
}
++proto_exprs;
continue;
}
if ((unify == e->string() || unify_fail == e->string()) && e1) {
if (!test_unify(e1, unify == e->string())) {
return false;
}
++proto_exprs;
continue;
}
#if !defined(SMTCOMP) && !defined(_EXTERNAL_RELEASE)
if ((kbo_lt == e->string() || kbo_gt == e->string() || kbo_eq == e->string() || kbo_un == e->string()) && e1) {
if (!test_kbo(e1, e->string())) {
return false;
}
++proto_exprs;
continue;
}
if ((lpo_lt == e->string() || lpo_gt == e->string() || lpo_eq == e->string() || lpo_un == e->string()) && e1) {
if (!test_lpo(e1, e->string())) {
return false;
}
++proto_exprs;
continue;
}
if ((st_insert == e->string() || st_erase == e->string()) && e1) {
if (!test_st(e1, e->string())) {
return false;
}
++proto_exprs;
continue;
}
if ((st_unify == e->string() || st_inst == e->string() || st_gen == e->string()) && e1) {
if (!test_st_visit(e1, e->string())) {
return false;
}
++proto_exprs;
continue;
}
if (st_reset == e->string()) {
m_st.reset();
++proto_exprs;
continue;
}
if (st_display == e->string()) {
m_st.display(std::cout);
++proto_exprs;
continue;
}
#endif
if (m_notes == e->string() && e1) {
++proto_exprs;
continue;
}
if ((source == e->string() || difficulty == e->string() || category == e->string()) && e1) {
++proto_exprs;
continue;
}
if (e->string() != empty) {
set_error("ignoring unknown attribute '", e->string().bare_str(), "'", e);
if (e1) {
++proto_exprs;
}
// do not fail.
// success = false;
continue;
}
TRACE("smtparser",
tout << "skipping: " << e->string() << " " <<
e->line() << " " <<
e->pos() << ".\n";);
continue;
}
return success;
}
bool is_id_token(proto_expr* expr) {
return
expr &&
(expr->kind() == proto_expr::ID ||
expr->kind() == proto_expr::STRING ||
expr->kind() == proto_expr::ANNOTATION);
}
smt_cmd_token get_command_token(cmd_context& ctx, proto_expr* expr) {
if (!expr) {
return SMT_CMD_ERROR;
}
if (!is_id_token(expr)) {
return SMT_CMD_ERROR;
}
return ctx.string2token(expr->string().bare_str());
}
bool process_command(cmd_context& cmd_ctx, proto_expr* p_expr) {
proto_expr* const* chs = p_expr->children();
proto_expr* e0 = chs?chs[0]:0;
proto_expr* e1 = e0?chs[1]:0;
std::ostream& out = cmd_ctx.get_out();
Z3_context ctx = cmd_ctx.get_context();
smt_cmd_token cmd_tok;
if (p_expr->kind() == proto_expr::ID) {
cmd_tok = get_command_token(cmd_ctx, p_expr);
}
else {
cmd_tok = get_command_token(cmd_ctx, e0);
}
switch(cmd_tok) {
case SMT_CMD_SET_LOGIC:
if (!check_id(e1)) {
cmd_ctx.print_failure("logic identifier expected as argument to logic", p_expr);
break;
}
if (Z3_set_logic(ctx, e1->string().bare_str())) {
// m_smtlib_logic param is only used for pretty printing.
Z3_get_parameters(ctx).m_smtlib_logic = e1->string().bare_str();
set_default_num_sort(e1->string());
cmd_ctx.print_success();
}
else {
cmd_ctx.print_failure("failed to set logic", p_expr);
}
break;
case SMT_CMD_DECLARE_SORTS:
if (!check_valid(cmd_ctx, p_expr, e1, "sort declaration expects an argument")) {
break;
}
if (e0 && e1 && chs[2]) {
cmd_ctx.print_failure("too many arguments passed to declaration", p_expr);
}
if (declare_sorts(e1)) {
cmd_ctx.print_success();
}
else {
cmd_ctx.print_failure("could not parse sort declaration", p_expr);
}
break;
case SMT_CMD_DECLARE_FUNS: // func-decls
if (!check_valid(cmd_ctx, p_expr, e1, "function declaration expects an argument")) {
break;
}
if (e0 && e1 && chs[2]) {
cmd_ctx.print_failure("too many arguments passed to declaration", p_expr);
}
if (declare_funs(e1)) {
cmd_ctx.print_success();
}
else {
cmd_ctx.print_failure("could not parse function declaration", p_expr);
}
break;
case SMT_CMD_DECLARE_PREDS: // pred-decls
if (!check_valid(cmd_ctx, p_expr, e1, "predicate declaration expects an argument")) {
break;
}
if (e0 && e1 && chs[2]) {
cmd_ctx.print_failure("too many arguments passed to declaration", p_expr);
}
if (declare_preds(e1)) {
cmd_ctx.print_success();
}
else {
cmd_ctx.print_failure("could not parse predicate declaration", p_expr);
}
break;
case SMT_CMD_DECLARE_DATATYPES:
if (!check_valid(cmd_ctx, p_expr, e1, "data-type declaration expects an argument")) {
break;
}
if (e0 && e1 && chs[2]) {
cmd_ctx.print_failure("too many arguments passed to declaration", p_expr);
}
if (declare_datatypes(e1)) {
cmd_ctx.print_success();
}
else {
cmd_ctx.print_failure("could not parse data-type declaration", p_expr);
}
break;
case SMT_CMD_DEFINE: {
expr_ref macro_expr(m_manager);
if (define_macro(cmd_ctx.get_table(), cmd_ctx.get_region(), p_expr, macro_expr)) {
cmd_ctx.add_trail(macro_expr);
cmd_ctx.print_success();
}
break;
}
case SMT_CMD_DEFINE_SORTS: {
if (!check_valid(cmd_ctx, p_expr, e1, "sort definition expects an argument")) {
break;
}
if (e0 && e1 && chs[2]) {
cmd_ctx.print_failure("too many arguments passed to declaration", p_expr);
}
if (define_sorts(e1)) {
cmd_ctx.print_success();
}
break;
}
case SMT_CMD_DECLARE_SORT: { // <symbol> <numeral>
if (!check_id(e1)) {
cmd_ctx.print_failure("identifier argument expected", p_expr);
break;
}
unsigned num_args = cmd_ctx.parse_opt_numeral(chs[2], 0);
if (e0 && e1 && chs[2] && chs[3]) {
cmd_ctx.print_failure("too many arguments passed to declaration", p_expr);
}
m_benchmark.get_symtable()->insert(e1->string(), alloc(user_sort, m_manager, num_args, e1->string()));
cmd_ctx.print_success();
break;
}
case SMT_CMD_DEFINE_SORT: { // <symbol> (<symbol>*) <sort>
if (!check_id(e1)) {
cmd_ctx.print_failure("sort definition expects three arguments", p_expr);
break;
}
proto_expr* e2 = chs[2];
if (!check_valid(cmd_ctx, p_expr, e2, "sort definition expects three arguments")) {
break;
}
proto_expr* e3 = chs[3];
if (!check_valid(cmd_ctx, p_expr, e3, "sort definition expects three arguments")) {
break;
}
if (chs[4]) {
cmd_ctx.print_failure("too many arguments passed to declaration", p_expr);
}
if (define_sort(e1, e2, e3)) {
cmd_ctx.print_success();
}
else {
cmd_ctx.print_failure("could not parse sort definition", p_expr);
}
break;
}
case SMT_CMD_DECLARE_FUN: { // <symbol> (<sort>*) <sort>
if (!check_id(e1)) {
cmd_ctx.print_failure("function declaration expects three arguments", p_expr);
break;
}
proto_expr* e2 = chs[2];
if (!check_valid(cmd_ctx, p_expr, e2, "function declaration expects three arguments")) {
break;
}
proto_expr* e3 = chs[3];
if (!check_valid(cmd_ctx, p_expr, e3, "function declaration expects three arguments")) {
break;
}
if (chs[4]) {
cmd_ctx.print_failure("too many arguments passed to declaration", p_expr);
}
if (declare_fun(e1,e2,e3)) {
cmd_ctx.print_success();
}
else {
cmd_ctx.print_failure("could not parse function declaration", p_expr);
}
break;
}
case SMT_CMD_DECLARE_CONST: { // <symbol> <sort>
if (!check_id(e1)) {
cmd_ctx.print_failure("constant declaration expects two arguments", p_expr);
break;
}
proto_expr* e2 = chs[2];
if (!check_valid(cmd_ctx, p_expr, e2, "constant declaration expects two arguments")) {
break;
}
if (chs[3]) {
cmd_ctx.print_failure("too many arguments passed to declaration", p_expr);
}
if (declare_fun(e1, 0, e2)) {
cmd_ctx.print_success();
}
else {
cmd_ctx.print_failure("could not parse constant declaration", p_expr);
}
break;
}
case SMT_CMD_DEFINE_FUN: { // <symbol> (<sorted_var>*) <sort> <term>
if (!check_id(e1)) {
cmd_ctx.print_failure("function definition expects four arguments", p_expr);
break;
}
proto_expr* e2 = chs[2];
if (!check_valid(cmd_ctx, p_expr, e2, "function definition expects four arguments")) {
break;
}
proto_expr* e3 = chs[3];
if (!check_valid(cmd_ctx, p_expr, e3, "function definition expects four arguments")) {
break;
}
proto_expr* e4 = chs[4];
if (!check_valid(cmd_ctx, p_expr, e4, "function definition expects four arguments")) {
break;
}
if (chs[5]) {
cmd_ctx.print_failure("too many arguments passed to declaration", p_expr);
}
expr_ref macro_expr(m_manager);
if (define_fun(cmd_ctx.get_table(), cmd_ctx.get_region(), e1, e2, e3, e4, macro_expr)) {
cmd_ctx.add_trail(macro_expr);
cmd_ctx.print_success();
}
else {
cmd_ctx.print_failure("could not parse function definition", p_expr);
}
break;
}
case SMT_CMD_GET_VALUE: { // (<term>+)
if (!check_valid(cmd_ctx, p_expr, e1, "get-value expects a list arguments")) {
break;
}
proto_expr* const* children = e1->children();
expr_ref_vector exprs(m_manager);
while (children && children[0]) {
expr_ref e(m_manager);
if (!get_expression(cmd_ctx, cmd_ctx.get_table(), p_expr, children[0], e, "one expression expected to eval")) {
break;
}
exprs.push_back(e);
++children;
}
cmd_ctx.eval(out, p_expr, exprs.size(), exprs.c_ptr());
break;
}
case SMT_CMD_PUSH: { // numeral
unsigned num_scopes = cmd_ctx.parse_opt_numeral(e1, 1);
cmd_ctx.push(num_scopes);
cmd_ctx.print_success();
if (e1 && chs[2]) {
cmd_ctx.print_failure("too many arguments passed to command", p_expr);
}
break;
}
case SMT_CMD_POP: { // numeral
unsigned num_scopes = cmd_ctx.parse_opt_numeral(e1, 1);
cmd_ctx.pop(num_scopes);
cmd_ctx.print_success();
if (e1 && chs[2]) {
cmd_ctx.print_failure("too many arguments passed to command", p_expr);
}
break;
}
case SMT_CMD_ASSERT: { // expr+
expr_ref_vector exprs(m_manager);
if (!chs) {
cmd_ctx.print_failure("expecting list of arguments", p_expr);
break;
}
if (!(*chs)) {
cmd_ctx.print_success();
break;
}
++chs;
if (!make_bool_expressions(cmd_ctx.get_table(), chs, exprs)) {
if (!cmd_ctx.has_error()) {
cmd_ctx.print_failure("could not parse expression", *chs);
}
return true;
}
for (unsigned i = 0; i < exprs.size(); ++i) {
cmd_ctx.add_asserted(exprs[i].get());
}
cmd_ctx.print_success();
break;
}
case SMT_CMD_CHECK_SAT: { // boolean-constants*
if (!chs || !(*chs)) {
cmd_ctx.check_sat(out);
}
else {
++chs;
if (!chs || !(*chs)) {
cmd_ctx.check_sat(out);
}
else {
expr_ref_vector exprs(m_manager);
if (!make_bool_expressions(cmd_ctx.get_table(), chs, exprs)) {
if (!cmd_ctx.has_error()) {
cmd_ctx.print_failure("could not parse expression", *chs);
}
return true;
}
cmd_ctx.get_core(p_expr, out, exprs.size(), exprs.c_ptr(), false);
}
}
break;
}
case SMT_CMD_GET_CORE: { // boolean-constants+
expr_ref_vector exprs(m_manager);
if (!chs) {
cmd_ctx.print_failure("expecting list of arguments", p_expr);
break;
}
if (!(*chs)) {
cmd_ctx.print_success();
break;
}
++chs;
if (!make_bool_expressions(cmd_ctx.get_table(), chs, exprs)) {
if (!cmd_ctx.has_error()) {
cmd_ctx.print_failure("could not parse expression", *chs);
}
return true;
}
cmd_ctx.get_core(p_expr, out, exprs.size(), exprs.c_ptr(), true); // just get the core
break;
}
case SMT_CMD_NEXT_SAT: { // <no arguments>
cmd_ctx.next_sat(out);
break;
}
case SMT_CMD_SIMPLIFY: {
expr_ref e(m_manager);
if (!get_expression(cmd_ctx, cmd_ctx.get_table(), p_expr, e1, e, "one expression expected to simplify")) {
break;
}
cmd_context::scoped_stopwatch stopw(cmd_ctx);
Z3_ast result = Z3_simplify(ctx, reinterpret_cast<Z3_ast>(e.get()));
out << Z3_ast_to_string(ctx, result) << "\n";
break;
}
case SMT_CMD_GET_IMPLIED_EQUALITIES: {
expr_ref_vector exprs(m_manager);
expr_ref e(m_manager);
if (!chs || !(*chs)) {
cmd_ctx.print_failure("expecting list of arguments", p_expr);
break;
}
++chs;
bool ok = true;
while (*chs) {
if (!get_expression(cmd_ctx, cmd_ctx.get_table(), p_expr, *chs, e, "list of expressions expected")) {
ok = false;
break;
}
exprs.push_back(e);
++chs;
}
if (!ok) {
break;
}
cmd_context::scoped_stopwatch stopw(cmd_ctx);
cmd_ctx.get_implied_equalities(out, exprs.size(), exprs.c_ptr());
break;
}
case SMT_CMD_EVAL: {
expr_ref e(m_manager);
if (!get_expression(cmd_ctx, cmd_ctx.get_table(), p_expr, e1, e, "one expression expected to eval")) {
break;
}
cmd_ctx.eval(out, p_expr, e);
break;
}
case SMT_CMD_GET_ASSERTIONS: { // string
scoped_stream strm(e1, out);
cmd_ctx.get_assertions(*strm);
break;
}
case SMT_CMD_GET_SAT_ASSERTIONS: { // string
scoped_stream strm(e1, out);
cmd_ctx.get_sat_assertions(out);
break;
}
case SMT_CMD_KEEP_SAT_ASSERTIONS: { //
Z3_ast assignment = Z3_get_context_assignment(ctx);
cmd_ctx.add_asserted(reinterpret_cast<expr*>(assignment));
cmd_ctx.print_success();
break;
}
case SMT_CMD_GET_UNSAT_CORE: { // string
scoped_stream strm(e1, out);
cmd_ctx.get_unsat_core(out, p_expr);
break;
}
case SMT_CMD_GET_PROOF: { // string
scoped_stream strm(e1, out);
cmd_ctx.get_proof(*strm, p_expr);
break;
}
case SMT_CMD_SET_OPTION: { // string strings
if (!e1) {
cmd_ctx.set_option_help(out, "");
break;
}
if (cmd_ctx.set_option(get_command_token(cmd_ctx,e1), chs+2)) {
cmd_ctx.print_success();
}
else {
cmd_ctx.print_unsupported();
}
break;
}
case SMT_CMD_SET_INFO: {
if (!e1) {
cmd_ctx.set_option_help(out, "");
break;
}
if (cmd_ctx.set_info(e1, chs+2)) {
cmd_ctx.print_success();
}
else {
cmd_ctx.print_unsupported();
}
break;
}
case SMT_CMD_GET_INFO: { // string+
if (!e1) {
cmd_ctx.get_info_help(out, "");
break;
}
++chs;
SASSERT(e1 == *chs);
out << "(";
while(*chs) {
if (!get_info(cmd_ctx, get_command_token(cmd_ctx,*chs))) {
out << ":" << chs[0]->string() << " \"unsupported\"";
}
++chs;
if (*chs) {
out << "\n";
}
}
out << ")\n";
break;
}
case SMT_CMD_ECHO: { // string+
if (!e1) {
cmd_ctx.get_info_help(out, "");
break;
}
++chs;
SASSERT(e1 == *chs);
while(*chs) {
out << chs[0]->string() << "\n";
++chs;
}
break;
}
case SMT_CMD_EXIT: // <no arguments>
return false;
case SMT_CMD_ERROR: // error token
cmd_ctx.print_failure("unrecognized command", p_expr);
break;
default:
if (get_info(cmd_ctx, cmd_tok)) {
out << "\n";
break;
}
if (cmd_ctx.get_command_help(out, cmd_tok)) {
out << "\n";
break;
}
cmd_ctx.print_failure("this is not a top-level command", p_expr);
break;
}
return true;
}
void flatten_exprs(expr_ref_vector& exprs) {
for (unsigned i = 0; i < exprs.size(); ++i) {
expr* e = exprs[i].get();
if (m_manager.is_and(e)) {
for (unsigned j = 1; j < to_app(e)->get_num_args(); ++j) {
exprs.push_back(to_app(e)->get_arg(j));
}
exprs[i] = to_app(e)->get_arg(0);
--i;
continue;
}
if (m_manager.is_not(e) &&
m_manager.is_or(to_app(e)->get_arg(0))) {
e = to_app(e)->get_arg(0);
app* a = to_app(e);
for (unsigned j = 1; j < a->get_num_args(); ++j) {
e = a->get_arg(j);
if (m_manager.is_not(e)) {
exprs.push_back(to_app(e)->get_arg(0));
}
else {
exprs.push_back(m_manager.mk_not(e));
}
}
e = a->get_arg(0);
if (m_manager.is_not(e)) {
exprs[i] = to_app(e)->get_arg(0);
}
else {
exprs[i] = m_manager.mk_not(e);
}
--i;
continue;
}
}
}
bool get_info(cmd_context& cmd_ctx, smt_cmd_token cmd_tok) {
return cmd_ctx.get_info(cmd_tok);
}
bool get_expression(cmd_context& cmd_ctx, symbol_table<idbuilder*>& table, proto_expr* p_expr, proto_expr* e1, expr_ref& e, char const* msg) {
if (!check_valid(cmd_ctx, p_expr, e1, msg)) {
return false;
}
m_binding_level = 0;
if (!make_expression(table, e1, e)) {
return false;
}
return true;
}
bool check_valid(cmd_context& cmd_ctx, proto_expr* p_expr, proto_expr* e, char const* msg) {
if (e == 0) {
cmd_ctx.print_failure(msg, p_expr);
}
return (e != 0);
}
bool check_id(proto_expr* e) {
return is_id_token(e);
}
bool make_expression(proto_expr * e, expr_ref & result) {
m_binding_level = 0;
symbol_table<idbuilder*> local_scope;
return make_expression(local_scope, e, result);
}
bool make_func_decl(proto_expr* e, func_decl_ref& result) {
func_decl* f;
if (m_benchmark.get_symtable()->find1(e->string(), f)) {
result = f;
return true;
}
else {
return false;
}
}
bool make_bool_expression(symbol_table<idbuilder*>& local_scope, proto_expr * e, expr_ref & result) {
if (!make_expression(local_scope, e, result)) {
return false;
}
if (!m_manager.is_bool(result)) {
set_error("expecting Boolean expression", e);
return false;
}
return true;
}
bool make_bool_expressions(symbol_table<idbuilder*>& local_scope, proto_expr * const* chs, expr_ref_vector & exprs) {
while (chs && *chs) {
expr_ref result(m_manager);
m_binding_level = 0;
if (!make_bool_expression(local_scope, *chs, result)) {
return false;
}
exprs.push_back(result);
++chs;
}
return true;
}
bool make_expression(symbol_table<idbuilder*>& local_scope, proto_expr * e, expr_ref & result) {
//
// Walk proto_expr by using the zipper.
// That is, maintain a stack of what's
// . left - already processed.
// . right - to be processed.
// . up - above the processed node.
//
region region;
expr_ref_vector * left = alloc(expr_ref_vector, m_manager);
proto_expr* const* right = 0;
ptr_vector<parse_frame> up;
proto_expr* current = e;
bool success = false;
idbuilder* builder = 0;
while (true) {
if (!current && right && *right) {
//
// pull the current from right.
//
current = *right;
++right;
}
if (!current && up.empty()) {
//
// we are done.
//
if (left->size() == 0) {
// set_error();
set_error("there are no expressions to return", e);
goto cleanup;
}
if (left->size() != 1) {
set_error("there are too many expressions to return", e);
goto cleanup;
}
result = left->back();
success = true;
goto cleanup;
}
if (!current && !up.empty()) {
//
// There is nothing more to process at this level.
//
// Apply the operator on the stack to the
// current 'left' vector.
// Adjust the stack by popping the left and right
// work-lists.
//
expr_ref term(m_manager);
parse_frame* above = up.back();
// symbol sym = above->get_proto_expr()->string();
if (above->make_term()) {
if (!above->make_term()->apply(*left, term)) {
set_error("Could not create application",
above->get_proto_expr());
success = false;
goto cleanup;
}
}
else if (!make_app(above->get_proto_expr(), *left, term)) {
success = false;
goto cleanup;
}
dealloc(left);
left = above->detach_left();
left->push_back(term.get());
right = above->right();
m_binding_level = above->binding_level();
up.pop_back();
continue;
}
while (current &&
current->kind() == proto_expr::CONS &&
current->children() &&
current->children()[0] &&
!current->children()[1]) {
current = current->children()[0];
}
switch(current->kind()) {
case proto_expr::ANNOTATION:
// ignore
current = 0;
break;
case proto_expr::ID: {
symbol const& id = current->string();
expr_ref term(m_manager);
expr * const_term = 0;
bool ok = true;
if (local_scope.find(id, builder)) {
expr_ref_vector tmp(m_manager);
if (!builder->apply(tmp, term)) {
set_error("identifier supplied with the wrong number of arguments ", id, current);
goto cleanup;
}
}
else if (m_benchmark.get_const(id, const_term)) {
// found.
term = const_term;
}
else if (is_builtin_const(id, current, current->num_params(), current->params(), ok, term)) {
if (!ok) goto cleanup;
}
else if (is_bvconst(id, current->num_params(), current->params(), term)) {
// found
}
else {
set_error("could not locate id ", id, current);
goto cleanup;
}
left->push_back(term.get());
current = 0;
break;
}
case proto_expr::STRING:
//
// Ignore strings.
//
current = 0;
break;
case proto_expr::COMMENT:
//
// Ignore comments.
//
current = 0;
break;
case proto_expr::INT:
left->push_back(mk_number(current->number(), true));
current = 0;
break;
case proto_expr::FLOAT:
left->push_back(mk_number(current->number(), false));
current = 0;
break;
case proto_expr::CONS:
if (!current->children() ||
!current->children()[0]) {
set_error("cons does not have children", current);
current = 0;
goto cleanup;
}
//
// expect the head to be a symbol
// which can be used to build a term of
// the subterms.
//
symbol const& head_symbol = current->children()[0]->string();
if (head_symbol == m_underscore) {
expr_ref term(m_manager);
proto_expr * const* chs = current->children() + 1;
symbol const& id = chs[0]->string();
sort_ref_vector sorts(m_manager);
vector<parameter> params;
bool ok = true;
if (!parse_params(chs+1, params, sorts)) {
goto cleanup;
}
if (is_builtin_const(id, current, params.size(), params.c_ptr(), ok, term)) {
if (!ok) goto cleanup;
}
else if (is_bvconst(id, params.size(), params.c_ptr(), term)) {
// ok
}
else {
set_error("Could not parse _ term", current);
goto cleanup;
}
left->push_back(term.get());
current = 0;
break;
}
if ((head_symbol == m_let) ||
(head_symbol == m_flet)) {
if (!current->children()[1] ||
!current->children()[2]) {
set_error("let does not have two arguments", current);
goto cleanup;
}
proto_expr * let_binding = current->children()[1];
proto_expr * const* let_body = current->children()+2;
//
// Collect bound variables and definitions for the bound variables
// into vectors 'vars' and 'bound'.
//
svector<symbol> vars;
ptr_vector<proto_expr> bound_vec;
if (is_binary_let_binding(let_binding)) {
vars.push_back(let_binding->children()[0]->string());
bound_vec.push_back(let_binding->children()[1]);
}
else {
proto_expr* const* children = let_binding->children();
if (!children) {
set_error("let binding does not have two arguments", let_binding);
goto cleanup;
}
while (*children) {
proto_expr* ch = *children;
if (!is_binary_let_binding(ch)) {
set_error("let binding does not have two arguments", ch);
goto cleanup;
}
vars.push_back(ch->children()[0]->string());
bound_vec.push_back(ch->children()[1]);
++children;
}
}
bound_vec.push_back(0);
proto_expr** bound = new (region) proto_expr*[bound_vec.size()];
for (unsigned i = 0; i < bound_vec.size(); ++i) {
bound[i] = bound_vec[i];
}
//
// Let's justify the transformation that
// pushes push_let and pop_let on the stack.
// and how it processes the let declaration.
//
// walk up left ((let ((v1 x1) (v2 x2)) z)::right)
//
// =
//
// walk (up::(pop_let(),left,right)::(bind(v1,v2),[],[z])) [] [x1;x2]
//
// = (* assume x1 -> y1, x2 -> y2 *)
//
// walk (up::(pop_let(),left,right)::(bind(v1,v2),[],[z])) [y1;y2] []
//
// = (* apply binding *)
//
// walk (up::(pop_let(),left,right)) [] [z]
//
// = (* assume z -> u *)
//
// walk up {left::u] right
//
// so if pop_let(v) [a,b] has the effect of removing v from the environment
// and projecting the second element "b", we obtain the effect of a let-binding.
//
expr_ref_vector * pinned = alloc(expr_ref_vector, m_manager);
pop_let * popl = new (region) pop_let(local_scope, pinned);
up.push_back(new (region) parse_frame(let_binding, popl, left, right, m_binding_level));
push_let_and * pushl = new (region) push_let_and(this, region, local_scope, pinned, vars.size(), vars.c_ptr());
expr_ref_vector * tmp = alloc(expr_ref_vector, m_manager);
up.push_back(new (region) parse_frame(let_binding, pushl, tmp, let_body, m_binding_level));
left = alloc(expr_ref_vector, m_manager);
right = bound;
current = 0;
break;
}
if (head_symbol == m_lblneg ||
head_symbol == m_lblpos) {
if (!current->children()[1] ||
!current->children()[2]) {
set_error("labels require two arguments", current);
goto cleanup;
}
bool is_pos = head_symbol == m_lblpos;
idbuilder* lbl = new (region) build_label(this, is_pos, current->children()[1]);
up.push_back(new (region) parse_frame(current, lbl, left, right, m_binding_level));
//
// process the body.
//
left = alloc(expr_ref_vector, m_manager);
right = 0;
current = current->children()[2];
break;
}
if (head_symbol == m_bang) {
proto_expr* const* children = current->children();
proto_expr* body = children[1];
proto_expr* lblname = 0;
bool is_pos = false;
children += 2;
while (children[0] &&
children[0]->kind() == proto_expr::ANNOTATION &&
children[1]) {
symbol id = children[0]->string();
if ((id == m_lblneg) ||
(id == m_lblpos)) {
is_pos = id == m_lblpos;
lblname = children[1];
}
children += 2;
}
if (lblname) {
idbuilder* lbl = new (region) build_label(this, is_pos, lblname);
up.push_back(new (region) parse_frame(current, lbl, left, right, m_binding_level));
left = alloc(expr_ref_vector, m_manager);
right = 0;
}
//
// process the body.
//
current = body;
break;
}
if ((head_symbol == m_forall) ||
(head_symbol == m_exists)) {
expr_ref_buffer patterns(m_manager);
expr_ref_buffer no_patterns(m_manager);
sort_ref_buffer sorts(m_manager);
svector<symbol> vars;
int weight = 1;
proto_expr* const* children = current->children();
proto_expr* body = 0;
++children;
if (!children[0] || !children[1]) {
set_error("quantifier should have at least two arguments", current);
goto cleanup;
}
//
// restore 'left' and 'right' working set and m_binding_level.
//
up.push_back(new (region) parse_frame(current, new (region) identity(), left, right, m_binding_level));
left = alloc(expr_ref_vector, m_manager);
//
// declare the bound variables.
//
local_scope.begin_scope();
while (children[0] && children[1] &&
(children[1]->kind() != proto_expr::ANNOTATION)) {
if (!parse_bound(local_scope, region, *children, vars, sorts)) {
goto cleanup;
}
++children;
}
body = children[0];
if (is_annotated_cons(body)) {
children = body->children()+1;
body = body->children()[1];
}
++children;
symbol qid = symbol(current->line());
symbol skid = symbol();
read_patterns(vars.size(), local_scope, children, patterns, no_patterns, weight, qid, skid);
//
// push a parse_frame to undo the scope of the quantifier.
//
SASSERT(sorts.size() > 0);
idbuilder* pop_q = new (region) pop_quantifier(this, (head_symbol == m_forall), weight, qid, skid, patterns, no_patterns, sorts, vars, local_scope, current);
expr_ref_vector * empty_v = alloc(expr_ref_vector, m_manager);
up.push_back(new (region) parse_frame(current, pop_q, empty_v, 0, m_binding_level));
//
// process the body.
//
right = 0;
current = body;
break;
}
if (is_underscore_op(region, current->children()[0], builder)) {
up.push_back(new (region) parse_frame(current, builder, left, right, m_binding_level));
}
else if (local_scope.find(head_symbol, builder)) {
up.push_back(new (region) parse_frame(current, builder, left, right, m_binding_level));
}
else {
up.push_back(new (region) parse_frame(current->children()[0], left, right, m_binding_level));
}
left = alloc(expr_ref_vector, m_manager);
right = current->children() + 1;
current = 0;
break;
}
}
cleanup:
if (success && !is_well_sorted(m_manager, result)) {
set_error("expression is not well sorted", e);
success = false;
}
dealloc(left);
while (!up.empty()) {
dealloc(up.back()->detach_left());
up.pop_back();
}
return success;
}
bool read_patterns(unsigned num_bindings, symbol_table<idbuilder*> & local_scope, proto_expr * const * & children,
expr_ref_buffer & patterns, expr_ref_buffer & no_patterns, int& weight, symbol& qid, symbol& skid) {
proto_region region;
while (children[0] &&
children[0]->kind() == proto_expr::ANNOTATION &&
children[1]) {
if (children[0]->string() == symbol("qid") ||
children[0]->string() == symbol("named")) {
qid = children[1]->string();
children += 2;
continue;
}
if (children[0]->string() == symbol("skolemid")) {
skid = children[1]->string();
children += 2;
continue;
}
ptr_vector<proto_expr> proto_exprs;
if (children[1]->kind() == proto_expr::COMMENT) {
std::string s = children[1]->string().str();
std::istringstream stream(s);
scanner scanner(stream, get_err(), false);
proto_expr_parser parser(region, scanner, get_err());
if (!parser.parse(proto_exprs)) {
set_error("could not parse expression", children[1]);
return false;
}
} else if (children[1]->kind() == proto_expr::CONS) {
for (proto_expr* const* pexpr = children[1]->children(); *pexpr; pexpr++)
proto_exprs.push_back(*pexpr);
} else {
proto_exprs.push_back(children[1]);
}
expr_ref_buffer ts(m_manager);
for (unsigned i = 0; i < proto_exprs.size(); ++i) {
expr_ref t(m_manager);
if (!make_expression(local_scope, proto_exprs[i], t)) {
return false;
}
ts.push_back(t.get());
}
if (children[0]->string() == symbol("pat") ||
children[0]->string() == symbol("pats") ||
children[0]->string() == symbol("pattern")) {
for (unsigned i = 0; i < ts.size(); ++i) {
if (!is_app(ts[i])) {
set_error("invalid pattern", children[0]);
return false;
}
}
expr * p = m_manager.mk_pattern(ts.size(), (app*const*)(ts.c_ptr()));
if (!p || (!ignore_user_patterns() && !m_pattern_validator(num_bindings, p))) {
set_error("invalid pattern", children[0]);
return false;
}
patterns.push_back(p);
}
else if (children[0]->string() == symbol("ex_act") && ts.size() == 1) {
app * sk_hack = m_manager.mk_app(m_sk_hack, 1, ts.c_ptr());
expr * p = m_manager.mk_pattern(1, &sk_hack);
if (!p || (!ignore_user_patterns() && !m_pattern_validator(num_bindings, p))) {
set_error("invalid pattern", children[0]);
return false;
}
patterns.push_back(p);
}
else if ((children[0]->string() == symbol("nopat") ||
children[0]->string() == symbol("no-pattern"))
&& ts.size() == 1) {
no_patterns.push_back(ts[0]);
}
else if (children[0]->string() == symbol("weight") && ts.size() == 1 &&
proto_exprs[0]->kind() == proto_expr::INT &&
proto_exprs[0]->number().is_unsigned()) {
weight = proto_exprs[0]->number().get_unsigned();
}
else {
// TODO: this should be a warning, perferably once per unknown kind of annotation
set_error("could not understand annotation '",
children[0]->string().bare_str(), "'", children[0]);
}
children += 2;
}
return true;
}
void set_default_num_sort(symbol const& name) {
if (name == symbol("QF_RDL") ||
name == symbol("QF_LRA") ||
name == symbol("LRA") ||
name == symbol("RDL") ||
name == symbol("QF_NRA") ||
name == symbol("QF_UFNRA") ||
name == symbol("QF_UFLRA")) {
m_int_sort = m_real_sort;
}
}
bool get_sort(theory* th, char const * s, sort_ref& sort) {
return make_sort(symbol(s), 0, 0, sort);
}
bool make_sort(symbol const & id, unsigned num_params, parameter const* params, sort_ref& s) {
builtin_op info;
if (m_builtin_sorts.find(id, info)) {
s = m_manager.mk_sort(info.m_family_id, info.m_kind, num_params, params);
return true;
}
if (num_params == 2 && symbol("Array") == id) {
// Old HACK to accomodate bit-vector arrays.
if (!params[0].is_int()) {
throw default_exception("Non-integer parameter to array");
return false;
}
if (!params[1].is_int()) {
throw default_exception("Non-integer parameter to array");
return false;
}
parameter bv_params0[1] = { parameter(params[0].get_int()) };
parameter bv_params1[1] = { parameter(params[1].get_int()) };
sort * t1 = m_manager.mk_sort(m_bv_fid, BV_SORT, 1, bv_params0);
sort * t2 = m_manager.mk_sort(m_bv_fid, BV_SORT, 1, bv_params1);
parameter params[2] = { parameter(t1), parameter(t2) };
s = m_manager.mk_sort(m_array_fid, ARRAY_SORT, 2, params);
return true;
}
sort* srt = 0;
if (m_benchmark.get_sort(id, srt)) {
s = srt;
return true;
}
return false;
}
bool make_sort(proto_expr * e, sort_ref& s) {
SASSERT(can_be_sort(e));
symtable& env = *m_benchmark.get_symtable();
sort_builder* mk_sort;
switch(e->kind()) {
case proto_expr::ID: {
if (make_sort(e->string(), e->num_params(), e->params(), s)) {
return true;
}
if (env.lookup(e->string(), mk_sort)) {
if (!mk_sort->apply(e->num_params(), e->params(), s)) {
set_error(mk_sort->error_message(), e);
return false;
}
return true;
}
set_error("could not find sort ", e);
return false;
}
case proto_expr::CONS: {
if (!can_be_sort(e)) {
set_error("expression cannot be a sort", e);
return false;
}
proto_expr *const* chs = e->children();
if (is_underscore(e)) {
++chs;
}
symbol name = (*chs)->string();
if (!env.lookup(name, mk_sort)) {
set_error("could not find sort symbol '", name.str(), "'", e);
return false;
}
sort_ref_vector sorts(m_manager);
vector<parameter> params;
if (!parse_params(chs+1, params, sorts)) {
return false;
}
if (!mk_sort->apply(params.size(), params.c_ptr(), s)) {
set_error(mk_sort->error_message(), e);
return false;
}
return true;
}
default:
set_error("could not create sort ", e);
return false;
}
}
bool parse_params(proto_expr* const* chs,vector<parameter>& params, sort_ref_vector& sorts) {
while (*chs) {
if ((*chs)->kind() == proto_expr::INT) {
rational const& num = (*chs)->number();
if (num.is_unsigned()) {
params.push_back(parameter(num.get_unsigned()));
}
else {
params.push_back(parameter(num));
}
}
else {
sort_ref s1(m_manager);
if (!make_sort(*chs, s1)) {
return false;
}
sorts.push_back(s1);
params.push_back(parameter((ast*)s1.get()));
}
++chs;
}
return true;
}
bool parse_bound(
symbol_table<idbuilder*>& local_scope,
region& region,
proto_expr* bound,
svector<symbol>& vars,
sort_ref_buffer& sorts
)
{
if (is_cons_list(bound)) {
proto_expr *const* children = bound->children();
while (*children) {
if (!parse_bound(local_scope, region, *children, vars, sorts)) {
return false;
}
++children;
}
return true;
}
if (!can_be_sorted_var(bound)) {
set_error("bound variable should contain a list of pairs", bound);
return false;
}
proto_expr* var = bound->children()[0];
proto_expr* sort_proto_expr = bound->children()[1];
sort_ref sort(m_manager);
if (!make_sort(sort_proto_expr, sort)) {
return false;
}
sorts.push_back(sort);
vars.push_back(var->string());
local_scope.insert(
var->string(),
new (region) bound_var(this, sort)
);
++m_binding_level;
return true;
}
bool can_be_sort(proto_expr* e) {
if (e && e->kind() == proto_expr::ID) {
return true;
}
if (is_underscore(e)) {
return true;
}
if (e &&
e->kind() == proto_expr::CONS &&
e->children() &&
e->children()[0] &&
e->children()[1]) {
proto_expr* const* ch = e->children();
while(*ch) {
if (!can_be_sort(*ch)) {
return false;
}
++ch;
}
return true;
}
return false;
}
bool declare_sorts(proto_expr* e) {
proto_expr* const * children = e->children();
while (children && *children) {
proto_expr* ch = *children;
switch(ch->kind()) {
case proto_expr::ID:
m_benchmark.declare_sort(ch->string());
break;
case proto_expr::CONS:
//
// The declaration of type constructors
// consists of an identifier together with
// a number indicating the arity of the
// constructor.
//
if (ch->children() &&
ch->children()[0] &&
ch->children()[0]->kind() == proto_expr::ID &&
ch->children()[1] &&
ch->children()[1]->kind() == proto_expr::INT) {
// unsigned num = (unsigned) ch->children()[1]->number().get_uint64();
m_benchmark.declare_sort(ch->children()[0]->string());
}
break;
case proto_expr::ANNOTATION:
break;
default:
set_error("unexpected argument to sorts",ch);
return false;
}
++children;
}
return true;
}
bool define_sorts(proto_expr* e) {
proto_expr* const * children = e->children();
while (children && *children) {
if (!define_sort(*children)) {
return false;
}
++children;
}
return true;
}
bool define_sort(proto_expr* e) {
proto_expr* const * children = e->children();
sort_ref_buffer domain(m_manager);
//
// First element in list must be an identifier.
// there should be just two elements.
//
if (!children ||
!children[0] ||
!(children[0]->kind() == proto_expr::ID) ||
!children[1] ||
children[2]) {
set_error("unexpected arguments to function declaration", e);
return false;
}
symbol name = children[0]->string();
sort_ref s(m_manager);
if (!can_be_sort(children[1]) ||
!make_sort(children[1], s)) {
set_error("unexpected arguments to function declaration", e);
return false;
}
m_benchmark.get_symtable()->insert(name, s);
return true;
}
bool declare_funs(proto_expr* e) {
proto_expr* const * children = e->children();
while (children && *children) {
if (!declare_fun(*children)) {
return false;
}
++children;
}
return true;
}
class define_sort_cls : public sort_builder {
smtparser& m_parser;
proto_region m_region;
proto_expr* m_expr;
svector<symbol> m_params;
symbol m_name;
std::string m_error_message;
public:
define_sort_cls(smtparser& p, symbol const& name, proto_expr* e, unsigned num_params, symbol* params) :
m_parser(p),
m_name(name) {
for (unsigned i = 0; i < num_params; ++i) {
m_params.push_back(params[i]);
}
m_expr = proto_expr::copy(m_region, e);
}
virtual bool apply(unsigned num_params, parameter const* params, sort_ref & result) {
smtlib::symtable * symtable = m_parser.m_benchmark.get_symtable();
if (m_params.size() != num_params) {
std::ostringstream strm;
strm << "wrong number of arguments passed to " << m_name << " "
<< m_params.size() << " expected, but " << num_params << " given";
m_error_message = strm.str();
return false;
}
for (unsigned i = 0; i < num_params; ++i) {
parameter p(params[i]);
if (!p.is_ast() || !is_sort(p.get_ast())) {
symtable->pop_sorts(i);
std::ostringstream strm;
strm << "argument " << i << " is not a sort";
m_error_message = strm.str();
return false;
}
symtable->push_sort(m_params[i], to_sort(p.get_ast()));
}
bool success = m_parser.make_sort(m_expr, result);
symtable->pop_sorts(num_params);
return success;
}
virtual char const* error_message() {
return m_error_message.c_str();
}
};
// (define-sort name (<symbol>*) <sort>)
bool define_sort(proto_expr* id, proto_expr* sorts, proto_expr* srt) {
symbol name = id->string();
proto_expr* const * children = sorts->children();
svector<symbol> names;
if (!children) {
set_error("Sort definition expects a list of sort symbols",id);
return false;
}
while (children[0]) {
id = children[0];
if(id->kind() != proto_expr::ID) {
set_error("unexpected argument, expected ID", id);
return false;
}
names.push_back(id->string());
++children;
}
m_benchmark.get_symtable()->insert(name, alloc(define_sort_cls, *this, name, srt, names.size(), names.c_ptr()));
return true;
}
bool declare_fun(proto_expr* id, proto_expr* sorts, proto_expr* srt) {
proto_expr* const * children = sorts?sorts->children():0;
sort_ref_buffer domain(m_manager);
symbol name = id->string();
if (sorts && !children) {
set_error("Function declaration expects a list of sorts", id);
return false;
}
//
// parse domain.
//
while (sorts && children[0]) {
sort_ref s(m_manager);
if (!make_sort(children[0], s)) {
return false;
}
domain.push_back(s);
++children;
}
sort_ref range(m_manager);
if (!make_sort(srt, range)) {
return false;
}
bool is_associative = false;
bool is_commutative = false;
bool is_injective = false;
m_benchmark.declare_func(name, domain, range, is_associative, is_commutative, is_injective);
return true;
}
bool declare_fun(proto_expr* e) {
proto_expr* const * children = e->children();
sort_ref_buffer domain(m_manager);
//
// Skip declaration of numbers.
//
if (children &&
children[0] &&
children[0]->kind() == proto_expr::INT) {
return true;
}
//
// First element in list must be an identifier.
//
if (!children ||
!children[0] ||
!(children[0]->kind() == proto_expr::ID)) {
set_error("unexpected arguments to function declaration", e);
return false;
}
symbol name = children[0]->string();
++children;
if (!can_be_sort(children[0])) {
set_error("unexpected arguments to function declaration", e);
return false;
}
//
// parse domain.
//
while (can_be_sort(children[1])) {
sort_ref s(m_manager);
if (!make_sort(children[0], s)) {
return false;
}
domain.push_back(s);
++children;
}
//
// parse range.
//
SASSERT(can_be_sort(children[0]));
sort_ref range(m_manager);
if (!make_sort(children[0], range)) {
return false;
}
++children;
//
// parse attributes.
//
bool is_associative = false;
bool is_commutative = false;
bool is_injective = false;
while(children[0] && children[0]->kind() == proto_expr::ANNOTATION) {
if (m_associative == children[0]->string()) {
is_associative = true;
}
else if (m_commutative == children[0]->string()) {
is_commutative = true;
}
else if (m_injective == children[0]->string()) {
is_injective = true;
}
++children;
}
m_benchmark.declare_func(name, domain, range, is_associative, is_commutative, is_injective);
return true;
}
bool declare_preds(proto_expr* e) {
proto_expr* const * children = e->children();
while (children && *children) {
if (!declare_pred(*children)) {
return false;
}
++children;
}
return true;
}
bool declare_pred(proto_expr* e) {
proto_expr* const * children = e->children();
if (!children || !children[0] || !(children[0]->kind() == proto_expr::ID)) {
set_error("unexpected arguments to predicate declaration", e);
return false;
}
symbol const & name = children[0]->string();
sort_ref_buffer domain(m_manager);
sort * bool_sort = m_manager.mk_bool_sort();
++children;
while (can_be_sort(children[0])) {
sort_ref s(m_manager);
if (!make_sort(children[0], s)) {
return false;
}
domain.push_back(s);
++children;
}
m_benchmark.declare_func(name, domain, bool_sort, false, false, false);
return true;
}
bool declare_datatypes(proto_expr * e) {
TRACE("datatypes", tout << "new datatype declarion section\n";);
proto_expr * const* children = e->children();
buffer<symbol> dt_names;
while (children && *children) {
proto_expr * type_decl = *children;
proto_expr * const* td_children = type_decl->children();
if (!td_children || !td_children[0] || !(td_children[0]->kind() == proto_expr::ID)) {
set_error("invalid datatype declaration", type_decl);
return false;
}
symbol name = td_children[0]->string();
sort * dummy;
if (m_benchmark.get_symtable()->find(name, dummy)) {
set_error("invalid datatype declaration, name was already used", type_decl);
return false;
}
dt_names.push_back(name);
TRACE("datatypes", tout << name << "\n";);
++children;
}
children = e->children();
ptr_buffer<datatype_decl> datatypes;
while (children && *children) {
datatype_decl * d = declare_datatype(*children, dt_names);
if (!d) {
return false;
}
datatypes.push_back(d);
++children;
}
sort_ref_vector new_types(m_manager);
bool result = m_dt_plugin->mk_datatypes(datatypes.size(), datatypes.c_ptr(), new_types);
del_datatype_decls(datatypes.size(), datatypes.c_ptr());
if (!result) {
set_error("invalid datatype declaration", e);
}
else {
unsigned num_types = new_types.size();
for (unsigned i = 0; i < num_types; i++) {
sort * d = new_types.get(i);
TRACE("datatype", tout << "new datatype\n" << mk_pp(d, m_manager) << "\n";
tout << "num. elements: " << d->get_num_elements() << "\n";
tout << "recursive: " << m_dt_util.is_recursive(d) << "\n";
tout << "non_rec constructor: " << m_dt_util.get_non_rec_constructor(d)->get_name() << "\n";
);
m_benchmark.insert(d);
ptr_vector<func_decl> const * constructors = m_dt_util.get_datatype_constructors(d);
unsigned num_constructors = constructors->size();
for (unsigned j = 0; j < num_constructors; j++) {
func_decl * c = constructors->get(j);
m_benchmark.insert(c);
func_decl * r = m_dt_util.get_constructor_recognizer(c);
TRACE("datatype",
tout << "new constructor\n" << mk_pp(c, m_manager) << "\n";
tout << "new recogniser\n" << mk_pp(r, m_manager) << "\n";);
m_benchmark.insert(r);
ptr_vector<func_decl> const * accessors = m_dt_util.get_constructor_accessors(c);
unsigned num_accessors = accessors->size();
for (unsigned k = 0; k < num_accessors; k++) {
func_decl * a = accessors->get(k);
TRACE("datatype", tout << "new accessor\n" << mk_pp(a, m_manager) << "\n";);
m_benchmark.insert(a);
}
}
}
}
return result;
}
datatype_decl * declare_datatype(proto_expr * e, buffer<symbol> const & dt_names) {
proto_expr* const * children = e->children();
symbol const& name = children[0]->string();
ptr_buffer<constructor_decl> constructors;
++children; // consume id
while (children && *children) {
constructor_decl * c = declare_constructor(*children, dt_names);
if (!c) {
del_constructor_decls(constructors.size(), constructors.c_ptr());
return false;
}
constructors.push_back(c);
++children;
}
if (constructors.size() == 0) {
set_error("datatype must have at least one constructor", e);
return false;
}
return mk_datatype_decl(name, constructors.size(), constructors.c_ptr());
}
constructor_decl * declare_constructor(proto_expr * e, buffer<symbol> const & dt_names) {
if (e->kind() == proto_expr::ID) {
symbol const & name = e->string();
string_buffer<> tmp;
tmp << "is_" << name;
symbol r_name(tmp.c_str());
return mk_constructor_decl(name, r_name, 0, 0);
}
proto_expr* const * children = e->children();
if (!children || !children[0] || !(children[0]->kind() == proto_expr::ID)) {
set_error("invalid constructor declaration", e);
return 0;
}
symbol const & name = children[0]->string();
string_buffer<> tmp;
tmp << "is_" << name;
symbol r_name(tmp.c_str());
ptr_buffer<accessor_decl> accessors;
++children; // skip id
while (children && *children) {
accessor_decl * d = declare_accessor(*children, dt_names);
if (!d) {
del_accessor_decls(accessors.size(), accessors.c_ptr());
return 0;
}
accessors.push_back(d);
++children;
}
return mk_constructor_decl(name, r_name, accessors.size(), accessors.c_ptr());
}
accessor_decl * declare_accessor(proto_expr * e, buffer<symbol> const & dt_names) {
proto_expr* const * children = e->children();
if (!children ||
!children[0] || !(children[0]->kind() == proto_expr::ID) ||
!children[1] || !(children[1]->kind() == proto_expr::ID) ||
children[2]) {
set_error("invalid accessor declaration", e);
return 0;
}
symbol const& name = children[0]->string();
symbol const& tname = children[1]->string();
unsigned tid = 0;
for (; tid < dt_names.size(); tid++) {
if (tname == dt_names[tid]) {
break;
}
}
type_ref ty;
if (tid < dt_names.size()) {
ty = type_ref(tid);
}
else {
sort_ref s(m_manager);
if (!make_sort(tname, children[1]->num_params(), children[1]->params(), s)) {
set_error("unknown sort", children[1]);
return 0;
}
m_pinned_sorts.push_back(s);
ty = type_ref(s.get());
}
return mk_accessor_decl(name, ty);
}
//
// (define-macro (name (x A) (y B)) body[x,y])
//
bool can_be_sorted_var(proto_expr* e) {
return
e &&
(e->kind() == proto_expr::CONS) &&
e->children() &&
e->children()[0] &&
(e->children()[0]->kind() == proto_expr::ID) &&
can_be_sort(e->children()[1]);
}
bool is_cons_list(proto_expr* e) {
return
e &&
(e->kind() == proto_expr::CONS) &&
e->children() &&
e->children()[0] &&
e->children()[0]->kind() == proto_expr::CONS;
}
bool is_prefixed(proto_expr* e, symbol const& s) {
return
e &&
(e->kind() == proto_expr::CONS) &&
e->children() &&
e->children()[0] &&
e->children()[1] &&
e->children()[0]->string() == s;
}
bool is_underscore(proto_expr* e) {
return
is_prefixed(e, m_underscore) &&
e->children()[1]->kind() == proto_expr::ID;
}
bool is_annotated_cons(proto_expr* e) {
return is_prefixed(e, m_bang);
}
bool is_builtin_const(symbol const& id, proto_expr* current, unsigned num_params, parameter * params, bool& ok, expr_ref& term) {
builtin_op info;
ok = true;
if (!m_builtin_ops.find(id, info)) {
return false;
}
fix_parameters(num_params, params);
func_decl* d = m_manager.mk_func_decl(info.m_family_id, info.m_kind, num_params, params, 0, (expr * const *)0);
if (!d) {
set_error("could not create a term from constant ", id, current);
ok = false;
}
else if (d->get_arity() != 0) {
set_error("identifier expects arguments ", id, current);
ok = false;
}
else {
term = m_manager.mk_const(d);
}
return true;
}
bool is_underscore_op(region& r, proto_expr* e, idbuilder*& builder) {
if (!is_underscore(e)) {
return false;
}
builtin_op info;
proto_expr *const* chs = e->children()+1;
symbol const& id = (*chs)->string();
sort_ref_vector sorts(m_manager);
vector<parameter> params;
if (!m_builtin_ops.find(id, info)) {
return false;
}
if (!parse_params(chs+1, params, sorts)) {
return false;
}
builder = new (r) builtin_builder(this, info.m_family_id, info.m_kind, params);
return true;
}
bool define_fun(symbol_table<idbuilder*>& table, region& r, proto_expr* name, proto_expr* args,
proto_expr* srt, proto_expr* body, expr_ref & macro_expr) {
symbol macro_name = name->string();
proto_expr* const* chs = args->children();
// parse marco arguments.
table.begin_scope();
ptr_vector<sort> sorts;
sort_ref sort1(m_manager), sort2(m_manager);
while (chs && *chs) {
proto_expr* e1 = *chs;
if (!can_be_sorted_var(e1)) {
set_error("Macro definition takes a list of pairs", e1);
goto error_cleanup;
}
proto_expr* var = e1->children()[0];
proto_expr* srt = e1->children()[1];
sort_ref sort(m_manager);
if (!make_sort(srt, sort)) {
goto error_cleanup;
}
sorts.push_back(sort);
m_pinned_sorts.push_back(sort);
table.insert(var->string(), new (r) bound_var(this, sort));
++chs;
++m_binding_level;
}
if (!make_expression(table, body, macro_expr)) {
goto error_cleanup;
}
if (!make_sort(srt, sort1)) {
goto error_cleanup;
}
sort2 = m_manager.get_sort(macro_expr);
if (sort1.get() != sort2.get()) {
std::ostringstream strm;
strm << "The expected sort for macro was " << mk_pp(sort1, m_manager)
<< " but the macro body has sort " << mk_pp(sort2, m_manager);
set_error(strm.str().c_str(), body);
goto error_cleanup;
}
table.end_scope();
m_binding_level = 0;
table.insert(macro_name, new (r) macro_builder(r, body, macro_expr, this, sorts.size(), sorts.c_ptr()));
return true;
error_cleanup:
table.end_scope();
m_binding_level = 0;
return false;
}
bool define_macro(symbol_table<idbuilder*>& table, region& r, proto_expr* macro_defn, expr_ref & macro_expr) {
SASSERT(macro_defn);
proto_expr* const* exprs = macro_defn->children();
proto_expr* e0 = exprs?exprs[0]:0;
proto_expr* e1 = e0?exprs[1]:0;
proto_expr* e2 = e1?exprs[2]:0;
m_binding_level = 0;
if (!e1) {
set_error("macro definition requires two arguments, none given", macro_defn);
return false;
}
if (!e2) {
set_error("macro definition requires two arguments, only one given", macro_defn);
return false;
}
// parse macro name
symbol macro_name;
proto_expr* const* chs = e1->children();
if (e1->kind() == proto_expr::ID) {
macro_name = e1->string();
chs = 0;
}
else if (chs && chs[0] && chs[0]->kind() == proto_expr::ID) {
macro_name = chs[0]->string();
chs = chs + 1;
}
else {
set_error("first argument to macro definition should be a name or a name applied to arguments", e1);
return false;
}
// parse marco arguments.
table.begin_scope();
ptr_vector<sort> sorts;
while (chs && *chs) {
e1 = *chs;
if (!can_be_sorted_var(e1)) {
set_error("Macro definition takes a list of pairs", e1);
goto error_cleanup;
}
proto_expr* var = e1->children()[0];
proto_expr* srt = e1->children()[1];
sort_ref sort(m_manager);
if (!make_sort(srt, sort)) {
goto error_cleanup;
}
sorts.push_back(sort);
m_pinned_sorts.push_back(sort);
table.insert(var->string(), new (r) bound_var(this, sort));
++chs;
++m_binding_level;
}
if (!make_expression(table, e2, macro_expr)) {
goto error_cleanup;
}
table.end_scope();
m_binding_level = 0;
table.insert(macro_name, new (r) macro_builder(r, e2, macro_expr, this, sorts.size(), sorts.c_ptr()));
return true;
error_cleanup:
table.end_scope();
m_binding_level = 0;
return false;
}
void fix_parameters(unsigned num_params, parameter* params) {
for (unsigned i = 0; i < num_params; ++i) {
func_decl* d = 0;
sort* s = 0;
builtin_op info;
if (params[i].is_symbol() && m_benchmark.get_symtable()->find1(params[i].get_symbol(), d)) {
params[i] = parameter(d);
}
else if (params[i].is_symbol() && m_benchmark.get_symtable()->find(params[i].get_symbol(), s)) {
params[i] = parameter(s);
}
else if (params[i].is_symbol() && m_builtin_sorts.find(params[i].get_symbol(), info)) {
params[i] = parameter(m_manager.mk_sort(info.m_family_id, info.m_kind, 0, 0));
}
}
}
bool test_unify(proto_expr * e, bool expected) {
proto_expr* const * children = e->children();
if (!children || !children[0] || !children[1]) {
set_error("invalid unification problem", e);
}
expr_ref f1(m_manager), f2(m_manager);
if (!make_expression(children[0], f1) || !make_expression(children[1], f2))
return false;
unsigned num_vars1 = 0;
unsigned num_vars2 = 0;
if (is_forall(f1)) {
num_vars1 = to_quantifier(f1)->get_num_decls();
f1 = to_quantifier(f1)->get_expr();
}
if (is_forall(f2)) {
num_vars2 = to_quantifier(f2)->get_num_decls();
f2 = to_quantifier(f2)->get_expr();
}
substitution s(m_manager);
s.reserve(2, std::max(num_vars1, num_vars2));
unifier u(m_manager);
if (u(f1, f2, s)) {
std::cout << "unification: succeeded\n";
if (!expected) {
get_err() << "WRONG ANSWER\n";
UNREACHABLE();
}
unsigned deltas[2] = { 0, num_vars1 };
s.display(std::cout, 2, deltas);
}
else {
std::cout << "unification: failed\n";
if (expected) {
get_err() << "WRONG ANSWER\n";
UNREACHABLE();
}
}
return true;
}
void dump_substitution(expr_ref_buffer & s) {
unsigned sz = s.size();
for (unsigned i = 0; i < sz; i++) {
expr * t = s[i];
if (t)
std::cout << "VAR " << i << " -->\n" << mk_pp(t, m_manager) << "\n";
}
}
#if !defined(SMTCOMP) && !defined(_EXTERNAL_RELEASE)
bool test_order(proto_expr * e, order & ord, symbol expected) {
proto_expr* const * children = e->children();
if (!children || !children[0] || !children[1]) {
set_error("invalid unification problem", e);
}
expr_ref f1(m_manager), f2(m_manager);
if (!make_expression(children[0], f1) || !make_expression(children[1], f2))
return false;
unsigned num_vars1 = 0;
unsigned num_vars2 = 0;
if (is_forall(f1)) {
num_vars1 = to_quantifier(f1)->get_num_decls();
f1 = to_quantifier(f1)->get_expr();
}
if (is_forall(f2)) {
num_vars2 = to_quantifier(f2)->get_num_decls();
f2 = to_quantifier(f2)->get_expr();
}
ord.reserve(1, std::max(num_vars1, num_vars2));
order::result r = ord.compare(f1.get(), f2.get());
if ((r == order::UNCOMPARABLE && expected != symbol("kbo_un") && expected != symbol("lpo_un")) ||
(r == order::LESSER && expected != symbol("kbo_lt") && expected != symbol("lpo_lt")) ||
(r == order::GREATER && expected != symbol("kbo_gt") && expected != symbol("lpo_gt")) ||
(r == order::EQUAL && expected != symbol("kbo_eq") && expected != symbol("lpo_eq")) ||
r == order::UNKNOWN) {
get_err() << "WRONG ANSWER\n";
UNREACHABLE();
}
else
std::cout << "order: succeeded\n";
return true;
}
bool test_kbo(proto_expr * e, symbol expected) {
precedence * p = alloc(arbitrary_precedence);
kbo k(m_manager, p);
return test_order(e, k, expected);
}
bool test_lpo(proto_expr * e, symbol expected) {
precedence * ps[2] = { alloc(arity_precedence), alloc(arbitrary_precedence) };
precedence * p = alloc(lex_precedence, 2, ps);
lpo l(m_manager, p);
return test_order(e, l, expected);
}
bool test_st(proto_expr * e, symbol op) {
expr_ref f(m_manager);
if (!make_expression(e, f))
return false;
if (is_forall(f))
f = to_quantifier(f)->get_expr();
if (!is_app(f))
set_error("invalid st operation", e);
if (op == symbol("st_insert"))
m_st.insert(to_app(f));
else
m_st.erase(to_app(f));
return true;
}
class simple_st_visitor : public st_visitor {
unsigned m_delta;
public:
simple_st_visitor(substitution & s, unsigned d):st_visitor(s), m_delta(d) {}
virtual bool operator()(expr * e) {
std::cout << "found:\n" << mk_pp(e, m_subst.get_manager()) << "\n";
unsigned deltas[2] = { 0, m_delta };
std::cout << "substitution:\n";
// m_subst.display(std::cout); std::cout << "\n";
m_subst.display(std::cout, 2, deltas);
std::cout << "\n";
return true;
}
};
bool test_st_visit(proto_expr * e, symbol op) {
expr_ref f(m_manager);
if (!make_expression(e, f))
return false;
unsigned num_vars = 0;
if (is_forall(f)) {
num_vars = to_quantifier(f)->get_num_decls();
f = to_quantifier(f)->get_expr();
}
if (!is_app(f))
set_error("invalid st operation", e);
substitution s(m_manager);
s.reserve(3, std::max(num_vars, m_st.get_approx_num_regs()));
simple_st_visitor v(s, num_vars);
std::cout << "searching for " << op << ":\n" << mk_pp(f, m_manager) << "\n\n";
if (op == symbol("st_unify"))
m_st.unify(to_app(f), v);
else if (op == symbol("st_inst"))
m_st.inst(to_app(f), v);
else
m_st.gen(to_app(f), v);
std::cout << "done.\n";
return true;
}
#endif
bool declare_axioms(proto_expr * e) {
proto_expr* const * children = e->children();
while (children && *children) {
if (!declare_axiom(*children)) {
return false;
}
++children;
}
return true;
}
bool declare_axiom(proto_expr * e) {
expr_ref t(m_manager);
if (!make_expression(e, t) ||
!push_assumption(t.get())) {
return false;
}
return true;
}
bool make_app(proto_expr * proto_expr, expr_ref_vector const & args, expr_ref & result) {
symbol const& name = proto_expr->string();
ptr_vector<sort> sorts;
func_decl * d = 0;
smtlib::symtable * symtable = m_benchmark.get_symtable();
for (unsigned i = 0; i < args.size(); ++i) {
sorts.push_back(m_manager.get_sort(args.get(i)));
}
if (symtable->find_overload(name, sorts, d)) {
result = m_manager.mk_app(d, args.size(), args.c_ptr());
return true;
}
builtin_op info;
if (m_builtin_ops.find(name, info)) {
unsigned num_params = proto_expr->num_params();
parameter * params = proto_expr->params();
fix_parameters(num_params, params);
d = m_manager.mk_func_decl(info.m_family_id, info.m_kind, num_params, params, args.size(), args.c_ptr());
if (d) {
result = m_manager.mk_app(d, args.size(), args.c_ptr());
return true;
}
}
rational arg2_value;
bool arg2_is_int;
if (name == symbol("store") &&
args.size() == 3 &&
m_anum_util.is_numeral(args.get(2), arg2_value, arg2_is_int) &&
arg2_is_int) {
expr_ref_vector new_args(m_manager);
new_args.push_back(args.get(0));
new_args.push_back(args.get(1));
new_args.push_back(m_anum_util.mk_numeral(arg2_value, false));
sorts.reset();
for (unsigned i = 0; i < args.size(); ++i) {
sorts.push_back(m_manager.get_sort(new_args.get(i)));
}
if (symtable->find_overload(name, sorts, d)) {
result = m_manager.mk_app(d, new_args.size(), new_args.c_ptr());
return true;
}
}
error_prefix(proto_expr);
get_err() << "could not find overload for '" << name << "' ";
for (unsigned i = 0; i < sorts.size(); ++i) {
get_err() << "Argument: "
<< mk_pp(args.get(i), m_manager)
<< " has type "
<< mk_pp(sorts[i], m_manager)
<< ".\n";
}
return false;
}
class nullary : public idbuilder {
expr* m_expr;
smtparser* m_parser;
unsigned m_decl_level_save;
public:
nullary(expr* e, smtparser* p) : m_expr(e), m_parser(p), m_decl_level_save(p->m_binding_level) {}
virtual bool apply(expr_ref_vector const& args, expr_ref & result) {
unsigned decl_level = m_parser->m_binding_level;
SASSERT(decl_level >= m_decl_level_save);
shift_vars shifty(m_parser->m_manager);
shifty(m_expr, decl_level - m_decl_level_save, result);
return (args.size() == 0);
}
};
class macro_builder : public idbuilder {
proto_expr* m_p_expr;
expr* m_expr;
smtparser* m_parser;
unsigned m_num_sorts;
sort** m_sorts;
public:
macro_builder(region& r, proto_expr* p_expr, expr* e, smtparser* p, unsigned num_sorts, sort* const* sorts) :
m_p_expr(p_expr), m_expr(e), m_parser(p), m_num_sorts(num_sorts) {
m_sorts = new (r) sort*[num_sorts];
for (unsigned i = 0; i < num_sorts; ++i) {
m_sorts[i] = sorts[i];
}
}
virtual bool apply(expr_ref_vector const& args, expr_ref& result) {
ast_manager& m = m_parser->m_manager;
if (args.size() != m_num_sorts) {
m_parser->set_error("wrong number of arguments passed to macro", m_p_expr);
return false;
}
for (unsigned i = 0; i < m_num_sorts; ++i) {
if (m_sorts[i] != m.get_sort(args[i])) {
std::ostringstream strm;
strm << "sort miss-match for argument of macro. Expecting sort: ";
strm << mk_pp(m_sorts[i], m) << " instead argument ";
strm << mk_pp(args[i], m) << " with sort ";
strm << mk_pp(m.get_sort(args[i]), m);
m_parser->set_error(strm.str().c_str(), m_p_expr);
return false;
}
}
if (m_num_sorts == 0) {
result = m_expr;
}
else {
var_subst subst(m);
subst(m_expr, args.size(), args.c_ptr(), result);
}
return true;
}
};
class identity : public idbuilder {
public:
identity() {}
virtual bool apply(expr_ref_vector const & args, expr_ref & result) {
if (args.size() == 1) {
result = args.back();
return true;
}
else {
return false;
}
}
};
class parse_frame {
public:
parse_frame(proto_expr * e, idbuilder * make, expr_ref_vector * left, proto_expr * const* right, unsigned binding_level):
m_proto_expr(e),
m_make_term(make),
m_left(left),
m_right(right),
m_binding_level(binding_level) {
}
parse_frame(proto_expr * e, expr_ref_vector * left, proto_expr * const* right, unsigned binding_level):
m_proto_expr(e),
m_make_term(0),
m_left(left),
m_right(right),
m_binding_level(binding_level) {
}
expr_ref_vector * detach_left() {
expr_ref_vector * result = m_left;
SASSERT(m_left);
m_left = 0;
return result;
}
unsigned binding_level() const { return m_binding_level; }
proto_expr* const * right() const { return m_right; }
idbuilder* make_term() { return m_make_term; }
proto_expr* get_proto_expr() const { return m_proto_expr; }
~parse_frame() { dealloc(m_left); }
private:
proto_expr* m_proto_expr;
idbuilder* m_make_term;
expr_ref_vector * m_left;
proto_expr* const * m_right;
unsigned m_binding_level;
parse_frame & operator=(parse_frame const & other);
parse_frame(parse_frame const & other);
};
class build_label : public idbuilder {
bool m_pos;
symbol m_sym;
smtparser * m_smt;
public:
build_label(smtparser * smt, bool is_pos, proto_expr * sym): m_pos(is_pos), m_smt(smt) {
switch(sym->kind()) {
case proto_expr::ID:
case proto_expr::STRING:
m_sym = sym->string();
break;
case proto_expr::INT:
m_sym = symbol(sym->number().to_string().c_str());
break;
default:
UNREACHABLE();
break;
}
}
virtual bool apply(expr_ref_vector const & args, expr_ref & result) {
if (args.size() >= 1) {
result = m_smt->m_manager.mk_label(m_pos, m_sym, args.get(0));
return true;
}
else {
return false;
}
}
};
class pop_let : public idbuilder {
public:
pop_let(symbol_table<idbuilder*> & local_scope, expr_ref_vector* pinned = 0):
m_local_scope(local_scope),
m_pinned(pinned) {
}
virtual ~pop_let() {}
virtual bool apply(expr_ref_vector const & args, expr_ref & result) {
dealloc(m_pinned);
if (args.size() == 2) {
m_local_scope.end_scope();
result = args.get(1);
return true;
}
else {
return false;
}
}
private:
symbol_table<idbuilder*> & m_local_scope;
expr_ref_vector* m_pinned;
};
class push_let : public idbuilder {
smtparser* m_parser;
region & m_region;
symbol_table<idbuilder*> & m_local_scope;
symbol m_let_var;
public:
push_let(smtparser* p, region & region, symbol_table<idbuilder*> & local_scope, symbol const & let_var):
m_parser(p),
m_region(region),
m_local_scope(local_scope),
m_let_var(let_var) {
}
virtual bool apply(expr_ref_vector const & args, expr_ref & result) {
//
// . push a scope,
// . create a nullary function using the variable/term association.
// . return the (first) argument.
//
//
if (args.size() == 1) {
m_local_scope.begin_scope();
m_local_scope.insert(m_let_var, new (m_region) nullary(args.back(), m_parser));
result = args.back();
return true;
}
else {
return false;
}
}
};
// push multiple let bound variables.
class push_let_and : public idbuilder {
smtparser* m_parser;
region & m_region;
symbol_table<idbuilder*> & m_local_scope;
unsigned m_num_vars;
symbol* m_vars;
expr_ref_vector* m_pinned;
public:
push_let_and(smtparser* p, region & region, symbol_table<idbuilder*> & local_scope, expr_ref_vector* pinned, unsigned num_vars, symbol const* vars):
m_parser(p),
m_region(region),
m_local_scope(local_scope),
m_num_vars(num_vars),
m_vars(new (region) symbol[num_vars]),
m_pinned(pinned) {
for (unsigned i = 0; i < num_vars; ++i) {
m_vars[i] = vars[i];
}
}
virtual bool apply(expr_ref_vector const & args, expr_ref & result) {
if (args.size() != m_num_vars) {
return false;
}
//
// . push a scope,
// . create a nullary function using the variable/term association.
// . return the last argument (arbitrary).
//
m_local_scope.begin_scope();
for (unsigned i = 0; i < m_num_vars; ++i) {
m_local_scope.insert(m_vars[i], new (m_region) nullary(args[i], m_parser));
m_pinned->push_back(args[i]);
}
result = args.back();
return true;
}
};
class bound_var : public idbuilder {
public:
bound_var(smtparser * smt, sort * sort):
m_smt(smt),
m_decl_level(smt->m_binding_level),
m_sort(sort) {
}
virtual bool apply(expr_ref_vector const & args, expr_ref & result) {
SASSERT(m_smt->m_binding_level > m_decl_level);
unsigned idx = m_smt->m_binding_level - m_decl_level - 1;
result = m_smt->m_manager.mk_var(idx, m_sort);
return args.empty();
}
private:
smtparser * m_smt;
unsigned m_decl_level;
sort * m_sort;
};
class pop_quantifier : public idbuilder {
public:
pop_quantifier(smtparser * smt, bool is_forall, int weight, symbol const& qid, symbol const& skid, expr_ref_buffer & patterns, expr_ref_buffer & no_patterns, sort_ref_buffer & sorts,
svector<symbol>& vars, symbol_table<idbuilder*> & local_scope, proto_expr* p_expr):
m_smt(smt),
m_is_forall(is_forall),
m_weight(weight),
m_qid(qid),
m_skid(skid),
m_patterns(m_smt->m_manager),
m_no_patterns(m_smt->m_manager),
m_sorts(m_smt->m_manager),
m_local_scope(local_scope),
m_p_expr(p_expr) {
SASSERT(sorts.size() == vars.size());
m_vars.append(vars);
m_sorts.append(sorts);
m_patterns.append(patterns);
m_no_patterns.append(no_patterns);
}
virtual bool apply(expr_ref_vector const & args, expr_ref & result) {
if (args.size() != 1) {
return false;
}
m_local_scope.end_scope();
expr * body = args.back();
if (m_smt->ignore_user_patterns()) {
TRACE("pat_bug", tout << "ignoring user patterns...: " << m_patterns.size() << "\n";);
result = m_smt->m_manager.mk_quantifier(m_is_forall,
m_sorts.size(), // num_decls
m_sorts.c_ptr(), // decl_sorts
m_vars.begin(), // decl_names
body,
m_weight,
m_qid,
m_skid,
0,
0,
0,
0);
}
else if (!m_patterns.empty()) {
if (!m_no_patterns.empty()) {
m_smt->set_error("patterns were provided, ignoring :nopat attribute.", ((proto_expr*)0));
}
result = m_smt->m_manager.mk_quantifier(m_is_forall,
m_sorts.size(), // num_decls
m_sorts.c_ptr(), // decl_sorts
m_vars.begin(), // decl_names
body,
m_weight,
m_qid,
m_skid,
m_patterns.size(),
m_patterns.c_ptr(),
0,
0);
}
else {
result = m_smt->m_manager.mk_quantifier(m_is_forall,
m_sorts.size(), // num_decls
m_sorts.c_ptr(), // decl_sorts
m_vars.begin(), // decl_names
body,
m_weight,
m_qid,
m_skid,
0,
0,
m_no_patterns.size(),
m_no_patterns.c_ptr());
}
//
// reclaim memory resources on application.
//
m_vars.finalize();
m_sorts.finalize();
m_patterns.finalize();
m_no_patterns.finalize();
return true;
}
private:
smtparser* m_smt;
bool m_is_forall;
int m_weight;
symbol m_qid;
symbol m_skid;
expr_ref_buffer m_patterns;
expr_ref_buffer m_no_patterns;
sort_ref_buffer m_sorts;
svector<symbol> m_vars;
symbol_table<idbuilder*>& m_local_scope;
proto_expr* m_p_expr;
};
class builtin_builder : public idbuilder {
smtparser* m_smt;
family_id m_fid;
decl_kind m_kind;
vector<parameter> m_params;
public:
builtin_builder(smtparser* smt, family_id fid, decl_kind k,vector<parameter> const& p):
m_smt(smt),
m_fid(fid),
m_kind(k),
m_params(p)
{
}
virtual bool apply(expr_ref_vector const& args, expr_ref& result) {
ast_manager& m = m_smt->m_manager;
func_decl* d = m.mk_func_decl(m_fid, m_kind, m_params.size(), m_params.c_ptr(), args.size(), args.c_ptr());
if (d) {
result = m.mk_app(d, args.size(), args.c_ptr());
}
m_params.finalize();
return d != 0;
}
};
bool push_status(smtlib::benchmark::status status) {
m_benchmark.set_status( status);
return true;
}
expr * mk_number(rational const & r, bool is_int){
if (m_int_sort == m_real_sort) // integer constants should be mapped to real
is_int = false;
return m_anum_util.mk_numeral(r, is_int);
}
void push_benchmark(symbol const & name) {
m_benchmark.set_name(name);
}
bool push_assumption(expr * t) {
m_benchmark.add_axiom(t);
return true;
}
bool push_formula(expr * t) {
m_benchmark.add_formula(t);
return true;
}
bool is_binary_let_binding(proto_expr* let_binding) {
return
let_binding &&
let_binding->children() &&
let_binding->children()[0] &&
(let_binding->children()[0]->kind() == proto_expr::ID) &&
let_binding->children()[1] &&
!let_binding->children()[2];
}
bool is_bvconst(symbol const & fname, unsigned num_params, parameter const* params, expr_ref & term) {
rational n;
char const * str = fname.bare_str();
unsigned sz = 0;
if (strncmp(str, "bvbin", 5) == 0) {
str += 5;
n = rational(0);
while (*str == '1' || *str == '0') {
n *= rational(2);
n += rational(*str - '0');
++sz;
++str;
}
if (sz == 0) {
return false;
}
}
else if (strncmp(str, "bvhex", 5) == 0) {
n = rational(0);
str += 5;
while (('0' <= *str && *str <= '9') ||
('a' <= *str && *str <= 'f') ||
('A' <= *str && *str <= 'F')) {
n *= rational(16);
if ('0' <= *str && *str <= '9') {
n += rational(*str - '0');
}
else if ('a' <= *str && *str <= 'f') {
n += rational(10);
n += rational(*str - 'a');
}
else {
SASSERT('A' <= *str && *str <= 'F');
n += rational(10);
n += rational(*str - 'A');
}
sz += 4;
++str;
}
if (sz == 0) {
return false;
}
}
else if (strncmp(str, "bv", 2) == 0 && '0' <= *(str + 2) && *(str + 2) <= '9') {
n = rational(0);
str += 2;
while ('0' <= *str && *str <= '9') {
n *= rational(10);
n += rational(*str - '0');
++str;
}
if (num_params == 1) {
sz = params[0].get_int();
}
else {
sz = 32;
}
}
else {
return false;
}
term = m_bvnum_util.mk_numeral(n, sz);
return true;
}
};
parser * parser::create(ast_manager& ast_manager, bool ignore_user_patterns) {
return alloc(smtparser, ast_manager, ignore_user_patterns);
}