mirror of
https://github.com/Z3Prover/z3
synced 2025-04-22 16:45:31 +00:00
Merge branch 'master' of https://github.com/Z3Prover/z3
This commit is contained in:
commit
ea353d77e9
23 changed files with 936 additions and 272 deletions
|
@ -328,6 +328,9 @@ public:
|
|||
app * mk_numeral(sexpr const * p, unsigned i) {
|
||||
return plugin().mk_numeral(p, i);
|
||||
}
|
||||
app * mk_int(int i) {
|
||||
return mk_numeral(rational(i), true);
|
||||
}
|
||||
app * mk_le(expr * arg1, expr * arg2) const { return m_manager.mk_app(m_afid, OP_LE, arg1, arg2); }
|
||||
app * mk_ge(expr * arg1, expr * arg2) const { return m_manager.mk_app(m_afid, OP_GE, arg1, arg2); }
|
||||
app * mk_lt(expr * arg1, expr * arg2) const { return m_manager.mk_app(m_afid, OP_LT, arg1, arg2); }
|
||||
|
|
|
@ -708,6 +708,7 @@ struct nnf::imp {
|
|||
}
|
||||
|
||||
bool process_app(app * t, frame & fr) {
|
||||
TRACE("nnf", tout << mk_ismt2_pp(t, m()) << "\n";);
|
||||
SASSERT(m().is_bool(t));
|
||||
if (t->get_family_id() == m().get_basic_family_id()) {
|
||||
switch (static_cast<basic_op_kind>(t->get_decl_kind())) {
|
||||
|
|
|
@ -55,22 +55,33 @@ br_status seq_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * con
|
|||
return mk_seq_concat(args[0], args[1], result);
|
||||
case OP_SEQ_LENGTH:
|
||||
SASSERT(num_args == 1);
|
||||
return mk_str_length(args[0], result);
|
||||
return mk_seq_length(args[0], result);
|
||||
case OP_SEQ_EXTRACT:
|
||||
SASSERT(num_args == 3);
|
||||
return mk_str_substr(args[0], args[1], args[2], result);
|
||||
return mk_seq_extract(args[0], args[1], args[2], result);
|
||||
case OP_SEQ_CONTAINS:
|
||||
SASSERT(num_args == 2);
|
||||
return mk_str_strctn(args[0], args[1], result);
|
||||
return mk_seq_contains(args[0], args[1], result);
|
||||
case OP_SEQ_AT:
|
||||
SASSERT(num_args == 2);
|
||||
return mk_str_at(args[0], args[1], result);
|
||||
return mk_seq_at(args[0], args[1], result);
|
||||
case OP_SEQ_PREFIX:
|
||||
SASSERT(num_args == 2);
|
||||
return mk_seq_prefix(args[0], args[1], result);
|
||||
case OP_SEQ_SUFFIX:
|
||||
SASSERT(num_args == 2);
|
||||
return mk_seq_suffix(args[0], args[1], result);
|
||||
case OP_SEQ_INDEX:
|
||||
if (num_args == 2) {
|
||||
expr_ref arg3(m_autil.mk_int(0), m());
|
||||
result = m_util.str.mk_index(args[0], args[1], arg3);
|
||||
return BR_REWRITE1;
|
||||
}
|
||||
SASSERT(num_args == 3);
|
||||
return mk_seq_index(args[0], args[1], args[2], result);
|
||||
case OP_SEQ_REPLACE:
|
||||
SASSERT(num_args == 3);
|
||||
return mk_seq_replace(args[0], args[1], args[2], result);
|
||||
case OP_SEQ_TO_RE:
|
||||
return BR_FAILED;
|
||||
case OP_SEQ_IN_RE:
|
||||
|
@ -78,12 +89,6 @@ br_status seq_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * con
|
|||
|
||||
case OP_STRING_CONST:
|
||||
return BR_FAILED;
|
||||
case OP_STRING_STRIDOF:
|
||||
SASSERT(num_args == 3);
|
||||
return mk_str_stridof(args[0], args[1], args[2], result);
|
||||
case OP_STRING_STRREPL:
|
||||
SASSERT(num_args == 3);
|
||||
return mk_str_strrepl(args[0], args[1], args[2], result);
|
||||
case OP_STRING_ITOS:
|
||||
SASSERT(num_args == 1);
|
||||
return mk_str_itos(args[0], result);
|
||||
|
@ -101,7 +106,8 @@ br_status seq_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * con
|
|||
case _OP_STRING_IN_REGEXP:
|
||||
case _OP_STRING_TO_REGEXP:
|
||||
case _OP_STRING_SUBSTR:
|
||||
|
||||
case _OP_STRING_STRREPL:
|
||||
case _OP_STRING_STRIDOF:
|
||||
UNREACHABLE();
|
||||
}
|
||||
return BR_FAILED;
|
||||
|
@ -143,7 +149,7 @@ br_status seq_rewriter::mk_seq_concat(expr* a, expr* b, expr_ref& result) {
|
|||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status seq_rewriter::mk_str_length(expr* a, expr_ref& result) {
|
||||
br_status seq_rewriter::mk_seq_length(expr* a, expr_ref& result) {
|
||||
std::string b;
|
||||
m_es.reset();
|
||||
m_util.str.get_concat(a, m_es);
|
||||
|
@ -153,6 +159,12 @@ br_status seq_rewriter::mk_str_length(expr* a, expr_ref& result) {
|
|||
if (m_util.str.is_string(m_es[i], b)) {
|
||||
len += b.length();
|
||||
}
|
||||
else if (m_util.str.is_unit(m_es[i])) {
|
||||
len += 1;
|
||||
}
|
||||
else if (m_util.str.is_empty(m_es[i])) {
|
||||
// skip
|
||||
}
|
||||
else {
|
||||
m_es[j] = m_es[i];
|
||||
++j;
|
||||
|
@ -176,7 +188,7 @@ br_status seq_rewriter::mk_str_length(expr* a, expr_ref& result) {
|
|||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status seq_rewriter::mk_str_substr(expr* a, expr* b, expr* c, expr_ref& result) {
|
||||
br_status seq_rewriter::mk_seq_extract(expr* a, expr* b, expr* c, expr_ref& result) {
|
||||
std::string s;
|
||||
rational pos, len;
|
||||
if (m_util.str.is_string(a, s) && m_autil.is_numeral(b, pos) && m_autil.is_numeral(c, len) &&
|
||||
|
@ -188,22 +200,22 @@ br_status seq_rewriter::mk_str_substr(expr* a, expr* b, expr* c, expr_ref& resul
|
|||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
br_status seq_rewriter::mk_str_strctn(expr* a, expr* b, expr_ref& result) {
|
||||
br_status seq_rewriter::mk_seq_contains(expr* a, expr* b, expr_ref& result) {
|
||||
std::string c, d;
|
||||
if (m_util.str.is_string(a, c) && m_util.str.is_string(b, d)) {
|
||||
result = m().mk_bool_val(0 != strstr(d.c_str(), c.c_str()));
|
||||
result = m().mk_bool_val(0 != strstr(c.c_str(), d.c_str()));
|
||||
return BR_DONE;
|
||||
}
|
||||
// check if subsequence of a is in b.
|
||||
// check if subsequence of b is in a.
|
||||
ptr_vector<expr> as, bs;
|
||||
m_util.str.get_concat(a, as);
|
||||
m_util.str.get_concat(b, bs);
|
||||
bool found = false;
|
||||
for (unsigned i = 0; !found && i < bs.size(); ++i) {
|
||||
if (as.size() > bs.size() - i) break;
|
||||
for (unsigned i = 0; !found && i < as.size(); ++i) {
|
||||
if (bs.size() > as.size() - i) break;
|
||||
unsigned j = 0;
|
||||
for (; j < as.size() && as[j] == bs[i+j]; ++j) {};
|
||||
found = j == as.size();
|
||||
for (; j < bs.size() && as[j] == bs[i+j]; ++j) {};
|
||||
found = j == bs.size();
|
||||
}
|
||||
if (found) {
|
||||
result = m().mk_true();
|
||||
|
@ -212,7 +224,7 @@ br_status seq_rewriter::mk_str_strctn(expr* a, expr* b, expr_ref& result) {
|
|||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status seq_rewriter::mk_str_at(expr* a, expr* b, expr_ref& result) {
|
||||
br_status seq_rewriter::mk_seq_at(expr* a, expr* b, expr_ref& result) {
|
||||
std::string c;
|
||||
rational r;
|
||||
if (m_util.str.is_string(a, c) && m_autil.is_numeral(b, r) && r.is_unsigned()) {
|
||||
|
@ -228,7 +240,7 @@ br_status seq_rewriter::mk_str_at(expr* a, expr* b, expr_ref& result) {
|
|||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status seq_rewriter::mk_str_stridof(expr* a, expr* b, expr* c, expr_ref& result) {
|
||||
br_status seq_rewriter::mk_seq_index(expr* a, expr* b, expr* c, expr_ref& result) {
|
||||
std::string s1, s2;
|
||||
rational r;
|
||||
bool isc1 = m_util.str.is_string(a, s1);
|
||||
|
@ -249,7 +261,7 @@ br_status seq_rewriter::mk_str_stridof(expr* a, expr* b, expr* c, expr_ref& resu
|
|||
return BR_DONE;
|
||||
}
|
||||
|
||||
if (m_util.str.is_empty(b)) {
|
||||
if (m_util.str.is_empty(b) && m_autil.is_numeral(c, r) && r.is_zero()) {
|
||||
result = c;
|
||||
return BR_DONE;
|
||||
}
|
||||
|
@ -257,15 +269,17 @@ br_status seq_rewriter::mk_str_stridof(expr* a, expr* b, expr* c, expr_ref& resu
|
|||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status seq_rewriter::mk_str_strrepl(expr* a, expr* b, expr* c, expr_ref& result) {
|
||||
br_status seq_rewriter::mk_seq_replace(expr* a, expr* b, expr* c, expr_ref& result) {
|
||||
std::string s1, s2, s3;
|
||||
if (m_util.str.is_string(a, s1) && m_util.str.is_string(b, s2) &&
|
||||
m_util.str.is_string(c, s3)) {
|
||||
std::ostringstream buffer;
|
||||
bool can_replace = true;
|
||||
for (size_t i = 0; i < s1.length(); ) {
|
||||
if (strncmp(s1.c_str() + i, s2.c_str(), s2.length()) == 0) {
|
||||
if (can_replace && strncmp(s1.c_str() + i, s2.c_str(), s2.length()) == 0) {
|
||||
buffer << s3;
|
||||
i += s2.length();
|
||||
can_replace = false;
|
||||
}
|
||||
else {
|
||||
buffer << s1[i];
|
||||
|
@ -367,6 +381,7 @@ br_status seq_rewriter::mk_seq_prefix(expr* a, expr* b, expr_ref& result) {
|
|||
return BR_REWRITE3;
|
||||
}
|
||||
if (i > 0) {
|
||||
SASSERT(i < as.size() && i < bs.size());
|
||||
a = m_util.str.mk_concat(as.size() - i, as.c_ptr() + i);
|
||||
b = m_util.str.mk_concat(bs.size() - i, bs.c_ptr() + i);
|
||||
result = m_util.str.mk_prefix(a, b);
|
||||
|
@ -644,54 +659,127 @@ bool seq_rewriter::reduce_eq(expr* l, expr* r, expr_ref_vector& lhs, expr_ref_ve
|
|||
}
|
||||
|
||||
bool is_sat;
|
||||
if (!change) {
|
||||
if (is_subsequence(m_lhs.size(), m_lhs.c_ptr(), m_rhs.size(), m_rhs.c_ptr(), lhs, rhs, is_sat)) {
|
||||
return is_sat;
|
||||
}
|
||||
unsigned szl = m_lhs.size() - head1, szr = m_rhs.size() - head2;
|
||||
expr* const* ls = m_lhs.c_ptr() + head1, * const*rs = m_rhs.c_ptr() + head2;
|
||||
if (length_constrained(szl, ls, szr, rs, lhs, rhs, is_sat)) {
|
||||
return is_sat;
|
||||
}
|
||||
if (is_subsequence(szl, ls, szr, rs, lhs, rhs, is_sat)) {
|
||||
return is_sat;
|
||||
}
|
||||
|
||||
if (szl == 0 && szr == 0) {
|
||||
return true;
|
||||
}
|
||||
else if (!change) {
|
||||
lhs.push_back(l);
|
||||
rhs.push_back(r);
|
||||
}
|
||||
else if (head1 == m_lhs.size() && head2 == m_rhs.size()) {
|
||||
// skip
|
||||
}
|
||||
else if (head1 == m_lhs.size()) {
|
||||
return set_empty(m_rhs.size() - head2, m_rhs.c_ptr() + head2, lhs, rhs);
|
||||
}
|
||||
else if (head2 == m_rhs.size()) {
|
||||
return set_empty(m_lhs.size() - head1, m_lhs.c_ptr() + head1, lhs, rhs);
|
||||
}
|
||||
else { // could solve if either side is fixed size.
|
||||
SASSERT(head1 < m_lhs.size() && head2 < m_rhs.size());
|
||||
if (is_subsequence(m_lhs.size() - head1, m_lhs.c_ptr() + head1,
|
||||
m_rhs.size() - head2, m_rhs.c_ptr() + head2, lhs, rhs, is_sat)) {
|
||||
return is_sat;
|
||||
}
|
||||
else {
|
||||
// could solve if either side is fixed size.
|
||||
SASSERT(szl > 0 && szr > 0);
|
||||
|
||||
lhs.push_back(m_util.str.mk_concat(m_lhs.size() - head1, m_lhs.c_ptr() + head1));
|
||||
rhs.push_back(m_util.str.mk_concat(m_rhs.size() - head2, m_rhs.c_ptr() + head2));
|
||||
lhs.push_back(m_util.str.mk_concat(szl, ls));
|
||||
rhs.push_back(m_util.str.mk_concat(szr, rs));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool seq_rewriter::set_empty(unsigned sz, expr* const* es, expr_ref_vector& lhs, expr_ref_vector& rhs) {
|
||||
expr* seq_rewriter::concat_non_empty(unsigned n, expr* const* as) {
|
||||
SASSERT(n > 0);
|
||||
ptr_vector<expr> bs;
|
||||
for (unsigned i = 0; i < n; ++i) {
|
||||
if (m_util.str.is_unit(as[i]) ||
|
||||
m_util.str.is_string(as[i])) {
|
||||
bs.push_back(as[i]);
|
||||
}
|
||||
}
|
||||
if (bs.empty()) {
|
||||
return m_util.str.mk_empty(m().get_sort(as[0]));
|
||||
}
|
||||
else {
|
||||
return m_util.str.mk_concat(bs.size(), bs.c_ptr());
|
||||
}
|
||||
}
|
||||
|
||||
bool seq_rewriter::set_empty(unsigned sz, expr* const* es, bool all, expr_ref_vector& lhs, expr_ref_vector& rhs) {
|
||||
std::string s;
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
if (m_util.str.is_unit(es[i])) {
|
||||
return false;
|
||||
if (all) return false;
|
||||
}
|
||||
if (m_util.str.is_empty(es[i])) {
|
||||
else if (m_util.str.is_empty(es[i])) {
|
||||
continue;
|
||||
}
|
||||
if (m_util.str.is_string(es[i], s)) {
|
||||
SASSERT(s.length() > 0);
|
||||
return false;
|
||||
else if (m_util.str.is_string(es[i], s)) {
|
||||
if (all) {
|
||||
SASSERT(s.length() > 0);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
lhs.push_back(m_util.str.mk_empty(m().get_sort(es[i])));
|
||||
rhs.push_back(es[i]);
|
||||
}
|
||||
lhs.push_back(m_util.str.mk_empty(m().get_sort(es[i])));
|
||||
rhs.push_back(es[i]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool seq_rewriter::min_length(unsigned n, expr* const* es, size_t& len) {
|
||||
std::string s;
|
||||
bool bounded = true;
|
||||
len = 0;
|
||||
for (unsigned i = 0; i < n; ++i) {
|
||||
if (m_util.str.is_unit(es[i])) {
|
||||
++len;
|
||||
}
|
||||
else if (m_util.str.is_empty(es[i])) {
|
||||
continue;
|
||||
}
|
||||
else if (m_util.str.is_string(es[i], s)) {
|
||||
len += s.length();
|
||||
}
|
||||
else {
|
||||
bounded = false;
|
||||
}
|
||||
}
|
||||
return bounded;
|
||||
}
|
||||
|
||||
bool seq_rewriter::length_constrained(unsigned szl, expr* const* l, unsigned szr, expr* const* r,
|
||||
expr_ref_vector& lhs, expr_ref_vector& rhs, bool& is_sat) {
|
||||
is_sat = true;
|
||||
size_t len1 = 0, len2 = 0;
|
||||
bool bounded1 = min_length(szl, l, len1);
|
||||
bool bounded2 = min_length(szr, r, len2);
|
||||
if (bounded1 && len1 < len2) {
|
||||
is_sat = false;
|
||||
return true;
|
||||
}
|
||||
if (bounded2 && len2 < len1) {
|
||||
is_sat = false;
|
||||
return true;
|
||||
}
|
||||
if (bounded1 && len1 == len2 && len1 > 0) {
|
||||
is_sat = set_empty(szr, r, false, lhs, rhs);
|
||||
if (is_sat) {
|
||||
lhs.push_back(concat_non_empty(szl, l));
|
||||
rhs.push_back(concat_non_empty(szr, r));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (bounded2 && len1 == len2 && len1 > 0) {
|
||||
is_sat = set_empty(szl, l, false, lhs, rhs);
|
||||
if (is_sat) {
|
||||
lhs.push_back(concat_non_empty(szl, l));
|
||||
rhs.push_back(concat_non_empty(szr, r));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool seq_rewriter::is_subsequence(unsigned szl, expr* const* l, unsigned szr, expr* const* r,
|
||||
expr_ref_vector& lhs, expr_ref_vector& rhs, bool& is_sat) {
|
||||
is_sat = true;
|
||||
|
@ -720,13 +808,15 @@ bool seq_rewriter::is_subsequence(unsigned szl, expr* const* l, unsigned szr, ex
|
|||
if (rpos.contains(j)) {
|
||||
rs.push_back(r[j]);
|
||||
}
|
||||
else if (!set_empty(1, r + j, lhs, rhs)) {
|
||||
else if (!set_empty(1, r + j, true, lhs, rhs)) {
|
||||
is_sat = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
SASSERT(szl == rs.size());
|
||||
lhs.push_back(m_util.str.mk_concat(szl, l));
|
||||
rhs.push_back(m_util.str.mk_concat(szl, rs.c_ptr()));
|
||||
if (szl > 0) {
|
||||
lhs.push_back(m_util.str.mk_concat(szl, l));
|
||||
rhs.push_back(m_util.str.mk_concat(szl, rs.c_ptr()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -35,12 +35,12 @@ class seq_rewriter {
|
|||
ptr_vector<expr> m_es, m_lhs, m_rhs;
|
||||
|
||||
br_status mk_seq_concat(expr* a, expr* b, expr_ref& result);
|
||||
br_status mk_str_length(expr* a, expr_ref& result);
|
||||
br_status mk_str_substr(expr* a, expr* b, expr* c, expr_ref& result);
|
||||
br_status mk_str_strctn(expr* a, expr* b, expr_ref& result);
|
||||
br_status mk_str_at(expr* a, expr* b, expr_ref& result);
|
||||
br_status mk_str_stridof(expr* a, expr* b, expr* c, expr_ref& result);
|
||||
br_status mk_str_strrepl(expr* a, expr* b, expr* c, expr_ref& result);
|
||||
br_status mk_seq_length(expr* a, expr_ref& result);
|
||||
br_status mk_seq_extract(expr* a, expr* b, expr* c, expr_ref& result);
|
||||
br_status mk_seq_contains(expr* a, expr* b, expr_ref& result);
|
||||
br_status mk_seq_at(expr* a, expr* b, expr_ref& result);
|
||||
br_status mk_seq_index(expr* a, expr* b, expr* c, expr_ref& result);
|
||||
br_status mk_seq_replace(expr* a, expr* b, expr* c, expr_ref& result);
|
||||
br_status mk_seq_prefix(expr* a, expr* b, expr_ref& result);
|
||||
br_status mk_seq_suffix(expr* a, expr* b, expr_ref& result);
|
||||
br_status mk_str_itos(expr* a, expr_ref& result);
|
||||
|
@ -53,9 +53,14 @@ class seq_rewriter {
|
|||
br_status mk_re_plus(expr* a, expr_ref& result);
|
||||
br_status mk_re_opt(expr* a, expr_ref& result);
|
||||
|
||||
bool set_empty(unsigned sz, expr* const* es, expr_ref_vector& lhs, expr_ref_vector& rhs);
|
||||
bool set_empty(unsigned sz, expr* const* es, bool all, expr_ref_vector& lhs, expr_ref_vector& rhs);
|
||||
bool is_subsequence(unsigned n, expr* const* l, unsigned m, expr* const* r,
|
||||
expr_ref_vector& lhs, expr_ref_vector& rhs, bool& is_sat);
|
||||
bool length_constrained(unsigned n, expr* const* l, unsigned m, expr* const* r,
|
||||
expr_ref_vector& lhs, expr_ref_vector& rhs, bool& is_sat);
|
||||
bool min_length(unsigned n, expr* const* es, size_t& len);
|
||||
expr* concat_non_empty(unsigned n, expr* const* es);
|
||||
|
||||
public:
|
||||
seq_rewriter(ast_manager & m, params_ref const & p = params_ref()):
|
||||
m_util(m), m_autil(m) {
|
||||
|
|
|
@ -90,6 +90,7 @@ void seq_decl_plugin::match_left_assoc(psig& sig, unsigned dsz, sort *const* dom
|
|||
}
|
||||
bool is_match = true;
|
||||
for (unsigned i = 0; is_match && i < dsz; ++i) {
|
||||
SASSERT(dom[i]);
|
||||
is_match = match(binding, dom[i], sig.m_dom[0].get());
|
||||
}
|
||||
if (range && is_match) {
|
||||
|
@ -102,6 +103,7 @@ void seq_decl_plugin::match_left_assoc(psig& sig, unsigned dsz, sort *const* dom
|
|||
m.raise_exception(strm.str().c_str());
|
||||
}
|
||||
range_out = apply_binding(binding, sig.m_range);
|
||||
SASSERT(range_out);
|
||||
TRACE("seq_verbose", tout << mk_pp(range_out, m) << "\n";);
|
||||
}
|
||||
|
||||
|
@ -134,6 +136,7 @@ void seq_decl_plugin::match(psig& sig, unsigned dsz, sort *const* dom, sort* ran
|
|||
m.raise_exception(strm.str().c_str());
|
||||
}
|
||||
range_out = apply_binding(binding, sig.m_range);
|
||||
SASSERT(range_out);
|
||||
}
|
||||
|
||||
sort* seq_decl_plugin::apply_binding(ptr_vector<sort> const& binding, sort* s) {
|
||||
|
@ -177,6 +180,7 @@ void seq_decl_plugin::init() {
|
|||
sort* reAreA[2] = { reA, reA };
|
||||
sort* AA[2] = { A, A };
|
||||
sort* seqAint2T[3] = { seqA, intT, intT };
|
||||
sort* seq2AintT[3] = { seqA, seqA, intT };
|
||||
sort* str2T[2] = { strT, strT };
|
||||
sort* str3T[3] = { strT, strT, strT };
|
||||
sort* strTint2T[3] = { strT, intT, intT };
|
||||
|
@ -184,6 +188,7 @@ void seq_decl_plugin::init() {
|
|||
sort* strTreT[2] = { strT, reT };
|
||||
sort* str2TintT[3] = { strT, strT, intT };
|
||||
sort* seqAintT[2] = { seqA, intT };
|
||||
sort* seq3A[3] = { seqA, seqA, seqA };
|
||||
m_sigs.resize(LAST_SEQ_OP);
|
||||
// TBD: have (par ..) construct and load parameterized signature from premable.
|
||||
m_sigs[OP_SEQ_UNIT] = alloc(psig, m, "seq.unit", 1, 1, &A, seqA);
|
||||
|
@ -193,6 +198,8 @@ void seq_decl_plugin::init() {
|
|||
m_sigs[OP_SEQ_SUFFIX] = alloc(psig, m, "seq.suffixof", 1, 2, seqAseqA, boolT);
|
||||
m_sigs[OP_SEQ_CONTAINS] = alloc(psig, m, "seq.contains", 1, 2, seqAseqA, boolT);
|
||||
m_sigs[OP_SEQ_EXTRACT] = alloc(psig, m, "seq.extract", 1, 3, seqAint2T, seqA);
|
||||
m_sigs[OP_SEQ_REPLACE] = alloc(psig, m, "seq.replace", 1, 3, seq3A, seqA);
|
||||
m_sigs[OP_SEQ_INDEX] = alloc(psig, m, "seq.indexof", 1, 3, seq2AintT, intT);
|
||||
m_sigs[OP_SEQ_AT] = alloc(psig, m, "seq.at", 1, 2, seqAintT, seqA);
|
||||
m_sigs[OP_SEQ_LENGTH] = alloc(psig, m, "seq.len", 1, 1, &seqA, intT);
|
||||
m_sigs[OP_RE_PLUS] = alloc(psig, m, "re.+", 1, 1, &reA, reA);
|
||||
|
@ -209,8 +216,8 @@ void seq_decl_plugin::init() {
|
|||
m_sigs[OP_SEQ_TO_RE] = alloc(psig, m, "seq.to.re", 1, 1, &seqA, reA);
|
||||
m_sigs[OP_SEQ_IN_RE] = alloc(psig, m, "seq.in.re", 1, 2, seqAreA, boolT);
|
||||
m_sigs[OP_STRING_CONST] = 0;
|
||||
m_sigs[OP_STRING_STRIDOF] = alloc(psig, m, "str.indexof", 0, 3, str2TintT, intT);
|
||||
m_sigs[OP_STRING_STRREPL] = alloc(psig, m, "str.replace", 0, 3, str3T, strT);
|
||||
m_sigs[_OP_STRING_STRIDOF] = alloc(psig, m, "str.indexof", 0, 3, str2TintT, intT);
|
||||
m_sigs[_OP_STRING_STRREPL] = alloc(psig, m, "str.replace", 0, 3, str3T, strT);
|
||||
m_sigs[OP_STRING_ITOS] = alloc(psig, m, "int.to.str", 0, 1, &intT, strT);
|
||||
m_sigs[OP_STRING_STOI] = alloc(psig, m, "str.to.int", 0, 1, &strT, intT);
|
||||
m_sigs[OP_REGEXP_LOOP] = alloc(psig, m, "re.loop", 0, 2, strTint2T, reT); // maybe 3 arguments.
|
||||
|
@ -222,7 +229,7 @@ void seq_decl_plugin::init() {
|
|||
m_sigs[_OP_STRING_SUFFIX] = alloc(psig, m, "str.suffixof", 0, 2, str2T, boolT);
|
||||
m_sigs[_OP_STRING_IN_REGEXP] = alloc(psig, m, "str.in.re", 0, 2, strTreT, boolT);
|
||||
m_sigs[_OP_STRING_TO_REGEXP] = alloc(psig, m, "str.to.re", 0, 1, &strT, reT);
|
||||
m_sigs[_OP_STRING_SUBSTR] = alloc(psig, m, "str.substr", 0, 3, strTint2T, boolT);
|
||||
m_sigs[_OP_STRING_SUBSTR] = alloc(psig, m, "str.substr", 0, 3, strTint2T, strT);
|
||||
}
|
||||
|
||||
void seq_decl_plugin::set_manager(ast_manager* m, family_id id) {
|
||||
|
@ -282,6 +289,18 @@ func_decl* seq_decl_plugin::mk_str_fun(decl_kind k, unsigned arity, sort* const*
|
|||
return m.mk_func_decl(m_sigs[k]->m_name, arity, domain, rng, func_decl_info(m_family_id, k_seq));
|
||||
}
|
||||
|
||||
func_decl* seq_decl_plugin::mk_assoc_fun(decl_kind k, unsigned arity, sort* const* domain, sort* range, decl_kind k_seq, decl_kind k_string) {
|
||||
ast_manager& m = *m_manager;
|
||||
sort_ref rng(m);
|
||||
if (arity == 0) {
|
||||
m.raise_exception("Invalid function application. At least one argument expected");
|
||||
}
|
||||
match_left_assoc(*m_sigs[k], arity, domain, range, rng);
|
||||
func_decl_info info(m_family_id, k_seq);
|
||||
info.set_left_associative();
|
||||
return m.mk_func_decl(m_sigs[(rng == m_string)?k_string:k_seq]->m_name, rng, rng, rng, info);
|
||||
}
|
||||
|
||||
func_decl * seq_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters,
|
||||
unsigned arity, sort * const * domain, sort * range) {
|
||||
init();
|
||||
|
@ -301,18 +320,21 @@ func_decl * seq_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters,
|
|||
case OP_RE_STAR:
|
||||
case OP_RE_OPTION:
|
||||
case OP_RE_RANGE:
|
||||
case OP_RE_UNION:
|
||||
case OP_RE_EMPTY_SET:
|
||||
|
||||
case OP_RE_OF_PRED:
|
||||
case OP_STRING_ITOS:
|
||||
case OP_STRING_STOI:
|
||||
case OP_REGEXP_LOOP:
|
||||
match(*m_sigs[k], arity, domain, range, rng);
|
||||
return m.mk_func_decl(m_sigs[k]->m_name, arity, domain, rng, func_decl_info(m_family_id, k));
|
||||
|
||||
case OP_RE_LOOP:
|
||||
match(*m_sigs[k], arity, domain, range, rng);
|
||||
if (num_parameters != 2 || !parameters[0].is_int() || !parameters[1].is_int()) {
|
||||
m.raise_exception("Expecting two numeral parameters to function re-loop");
|
||||
}
|
||||
return m.mk_func_decl(m_sigs[k]->m_name, arity, domain, rng, func_decl_info(m_family_id, k, num_parameters, parameters));
|
||||
|
||||
case OP_STRING_CONST:
|
||||
if (!(num_parameters == 1 && arity == 0 && parameters[0].is_symbol())) {
|
||||
m.raise_exception("invalid string declaration");
|
||||
|
@ -320,33 +342,40 @@ func_decl * seq_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters,
|
|||
return m.mk_const_decl(m_stringc_sym, m_string,
|
||||
func_decl_info(m_family_id, OP_STRING_CONST, num_parameters, parameters));
|
||||
|
||||
case OP_SEQ_CONCAT: {
|
||||
if (arity == 0) {
|
||||
m.raise_exception("invalid concatenation. At least one argument expected");
|
||||
case OP_RE_UNION:
|
||||
return mk_assoc_fun(k, arity, domain, range, k, k);
|
||||
|
||||
case OP_RE_CONCAT:
|
||||
return mk_assoc_fun(k, arity, domain, range, k, k);
|
||||
|
||||
case OP_SEQ_CONCAT:
|
||||
return mk_assoc_fun(k, arity, domain, range, k, _OP_STRING_CONCAT);
|
||||
|
||||
case _OP_STRING_CONCAT:
|
||||
return mk_assoc_fun(k, arity, domain, range, OP_SEQ_CONCAT, k);
|
||||
|
||||
case OP_SEQ_REPLACE:
|
||||
return mk_seq_fun(k, arity, domain, range, _OP_STRING_STRREPL);
|
||||
case _OP_STRING_STRREPL:
|
||||
return mk_str_fun(k, arity, domain, range, OP_SEQ_REPLACE);
|
||||
|
||||
case OP_SEQ_INDEX:
|
||||
if (arity == 2) {
|
||||
sort* dom[3] = { domain[0], domain[1], arith_util(m).mk_int() };
|
||||
sort_ref rng(m);
|
||||
match(*m_sigs[k], 3, dom, range, rng);
|
||||
return m.mk_func_decl(m_sigs[(dom[0] == m_string)?_OP_STRING_STRIDOF:k]->m_name, arity, domain, rng, func_decl_info(m_family_id, k));
|
||||
}
|
||||
match_left_assoc(*m_sigs[k], arity, domain, range, rng);
|
||||
func_decl_info info(m_family_id, k);
|
||||
info.set_left_associative();
|
||||
return m.mk_func_decl(m_sigs[(rng == m_string)?_OP_STRING_CONCAT:k]->m_name, rng, rng, rng, info);
|
||||
}
|
||||
case OP_RE_CONCAT: {
|
||||
if (arity == 0) {
|
||||
m.raise_exception("invalid concatenation. At least one argument expected");
|
||||
return mk_seq_fun(k, arity, domain, range, _OP_STRING_STRIDOF);
|
||||
case _OP_STRING_STRIDOF:
|
||||
if (arity == 2) {
|
||||
sort* dom[3] = { domain[0], domain[1], arith_util(m).mk_int() };
|
||||
sort_ref rng(m);
|
||||
match(*m_sigs[k], 3, dom, range, rng);
|
||||
return m.mk_func_decl(m_sigs[k]->m_name, arity, domain, rng, func_decl_info(m_family_id, OP_SEQ_INDEX));
|
||||
}
|
||||
match_left_assoc(*m_sigs[k], arity, domain, range, rng);
|
||||
func_decl_info info(m_family_id, k);
|
||||
info.set_left_associative();
|
||||
return m.mk_func_decl(m_sigs[k]->m_name, rng, rng, rng, info);
|
||||
}
|
||||
case _OP_STRING_CONCAT: {
|
||||
if (arity == 0) {
|
||||
m.raise_exception("invalid concatenation. At least one argument expected");
|
||||
}
|
||||
match_left_assoc(*m_sigs[k], arity, domain, range, rng);
|
||||
func_decl_info info(m_family_id, OP_SEQ_CONCAT);
|
||||
info.set_left_associative();
|
||||
return m.mk_func_decl(m_sigs[k]->m_name, rng, rng, rng, info);
|
||||
}
|
||||
return mk_str_fun(k, arity, domain, range, OP_SEQ_INDEX);
|
||||
|
||||
case OP_SEQ_PREFIX:
|
||||
return mk_seq_fun(k, arity, domain, range, _OP_STRING_PREFIX);
|
||||
case _OP_STRING_PREFIX:
|
||||
|
@ -387,16 +416,13 @@ func_decl * seq_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters,
|
|||
case _OP_STRING_SUBSTR:
|
||||
return mk_str_fun(k, arity, domain, range, OP_SEQ_EXTRACT);
|
||||
|
||||
case OP_STRING_STRIDOF:
|
||||
case OP_STRING_STRREPL:
|
||||
case OP_STRING_ITOS:
|
||||
case OP_STRING_STOI:
|
||||
case OP_REGEXP_LOOP:
|
||||
match(*m_sigs[k], arity, domain, range, rng);
|
||||
return m.mk_func_decl(m_sigs[k]->m_name, arity, domain, rng, func_decl_info(m_family_id, k));
|
||||
|
||||
case _OP_SEQ_SKOLEM:
|
||||
return m.mk_func_decl(symbol("seq.skolem"), arity, domain, rng, func_decl_info(m_family_id, k));
|
||||
case _OP_SEQ_SKOLEM: {
|
||||
if (num_parameters != 1 || !parameters[0].is_symbol()) {
|
||||
m.raise_exception("one symbol parameter expected to skolem symbol");
|
||||
}
|
||||
symbol s = parameters[0].get_symbol();
|
||||
return m.mk_func_decl(s, arity, domain, range, func_decl_info(m_family_id, k, num_parameters, parameters));
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
|
@ -431,6 +457,7 @@ bool seq_decl_plugin::is_value(app* e) const {
|
|||
}
|
||||
|
||||
app* seq_util::mk_skolem(symbol const& name, unsigned n, expr* const* args, sort* range) {
|
||||
SASSERT(range);
|
||||
parameter param(name);
|
||||
func_decl* f = m.mk_func_decl(get_family_id(), _OP_SEQ_SKOLEM, 1, ¶m, n, args, range);
|
||||
return m.mk_app(f, n, args);
|
||||
|
|
|
@ -39,8 +39,10 @@ enum seq_op_kind {
|
|||
OP_SEQ_SUFFIX,
|
||||
OP_SEQ_CONTAINS,
|
||||
OP_SEQ_EXTRACT,
|
||||
OP_SEQ_REPLACE,
|
||||
OP_SEQ_AT,
|
||||
OP_SEQ_LENGTH,
|
||||
OP_SEQ_LENGTH,
|
||||
OP_SEQ_INDEX,
|
||||
OP_SEQ_TO_RE,
|
||||
OP_SEQ_IN_RE,
|
||||
|
||||
|
@ -59,12 +61,11 @@ enum seq_op_kind {
|
|||
|
||||
// string specific operators.
|
||||
OP_STRING_CONST,
|
||||
OP_STRING_STRIDOF, // TBD generalize
|
||||
OP_STRING_STRREPL, // TBD generalize
|
||||
OP_STRING_ITOS,
|
||||
OP_STRING_STOI,
|
||||
OP_REGEXP_LOOP, // TBD re-loop: integers as parameters or arguments?
|
||||
// internal only operators. Converted to SEQ variants.
|
||||
_OP_STRING_STRREPL,
|
||||
_OP_STRING_CONCAT,
|
||||
_OP_STRING_LENGTH,
|
||||
_OP_STRING_STRCTN,
|
||||
|
@ -74,6 +75,7 @@ enum seq_op_kind {
|
|||
_OP_STRING_TO_REGEXP,
|
||||
_OP_STRING_CHARAT,
|
||||
_OP_STRING_SUBSTR,
|
||||
_OP_STRING_STRIDOF,
|
||||
_OP_SEQ_SKOLEM,
|
||||
LAST_SEQ_OP
|
||||
};
|
||||
|
@ -114,6 +116,7 @@ class seq_decl_plugin : public decl_plugin {
|
|||
|
||||
func_decl* mk_seq_fun(decl_kind k, unsigned arity, sort* const* domain, sort* range, decl_kind k_string);
|
||||
func_decl* mk_str_fun(decl_kind k, unsigned arity, sort* const* domain, sort* range, decl_kind k_seq);
|
||||
func_decl* mk_assoc_fun(decl_kind k, unsigned arity, sort* const* domain, sort* range, decl_kind k_string, decl_kind k_seq);
|
||||
|
||||
void init();
|
||||
|
||||
|
@ -156,8 +159,10 @@ public:
|
|||
bool is_string(sort* s) const { return is_seq(s) && seq.is_char(s->get_parameter(0).get_ast()); }
|
||||
bool is_seq(sort* s) const { return is_sort_of(s, m_fid, SEQ_SORT); }
|
||||
bool is_re(sort* s) const { return is_sort_of(s, m_fid, RE_SORT); }
|
||||
bool is_re(sort* s, sort*& seq) const { return is_sort_of(s, m_fid, RE_SORT) && (seq = to_sort(s->get_parameter(0).get_ast()), true); }
|
||||
bool is_seq(expr* e) const { return is_seq(m.get_sort(e)); }
|
||||
bool is_re(expr* e) const { return is_re(m.get_sort(e)); }
|
||||
bool is_re(expr* e, sort*& seq) const { return is_re(m.get_sort(e), seq); }
|
||||
|
||||
app* mk_skolem(symbol const& name, unsigned n, expr* const* args, sort* range);
|
||||
bool is_skolem(expr const* e) const { return is_app_of(e, m_fid, _OP_SEQ_SKOLEM); }
|
||||
|
@ -175,12 +180,16 @@ public:
|
|||
app* mk_string(char const* s) { return mk_string(symbol(s)); }
|
||||
app* mk_string(std::string const& s) { return mk_string(symbol(s.c_str())); }
|
||||
app* mk_concat(expr* a, expr* b) { expr* es[2] = { a, b }; return m.mk_app(m_fid, OP_SEQ_CONCAT, 2, es); }
|
||||
app* mk_concat(expr* a, expr* b, expr* c) {
|
||||
return mk_concat(mk_concat(a, b), c);
|
||||
}
|
||||
expr* mk_concat(unsigned n, expr* const* es) { if (n == 1) return es[0]; SASSERT(n > 1); return m.mk_app(m_fid, OP_SEQ_CONCAT, n, es); }
|
||||
app* mk_length(expr* a) { return m.mk_app(m_fid, OP_SEQ_LENGTH, 1, &a); }
|
||||
app* mk_substr(expr* a, expr* b, expr* c) { expr* es[3] = { a, b, c }; return m.mk_app(m_fid, OP_SEQ_EXTRACT, 3, es); }
|
||||
app* mk_contains(expr* a, expr* b) { expr* es[2] = { a, b }; return m.mk_app(m_fid, OP_SEQ_CONTAINS, 2, es); }
|
||||
app* mk_prefix(expr* a, expr* b) { expr* es[2] = { a, b }; return m.mk_app(m_fid, OP_SEQ_PREFIX, 2, es); }
|
||||
app* mk_suffix(expr* a, expr* b) { expr* es[2] = { a, b }; return m.mk_app(m_fid, OP_SEQ_SUFFIX, 2, es); }
|
||||
app* mk_index(expr* a, expr* b, expr* i) { expr* es[3] = { a, b, i}; return m.mk_app(m_fid, OP_SEQ_INDEX, 3, es); }
|
||||
|
||||
|
||||
bool is_string(expr const * n) const { return is_app_of(n, m_fid, OP_STRING_CONST); }
|
||||
|
@ -200,8 +209,8 @@ public:
|
|||
bool is_extract(expr const* n) const { return is_app_of(n, m_fid, OP_SEQ_EXTRACT); }
|
||||
bool is_contains(expr const* n) const { return is_app_of(n, m_fid, OP_SEQ_CONTAINS); }
|
||||
bool is_at(expr const* n) const { return is_app_of(n, m_fid, OP_SEQ_AT); }
|
||||
bool is_stridof(expr const* n) const { return is_app_of(n, m_fid, OP_STRING_STRIDOF); }
|
||||
bool is_repl(expr const* n) const { return is_app_of(n, m_fid, OP_STRING_STRREPL); }
|
||||
bool is_index(expr const* n) const { return is_app_of(n, m_fid, OP_SEQ_INDEX); }
|
||||
bool is_replace(expr const* n) const { return is_app_of(n, m_fid, OP_SEQ_REPLACE); }
|
||||
bool is_prefix(expr const* n) const { return is_app_of(n, m_fid, OP_SEQ_PREFIX); }
|
||||
bool is_suffix(expr const* n) const { return is_app_of(n, m_fid, OP_SEQ_SUFFIX); }
|
||||
bool is_itos(expr const* n) const { return is_app_of(n, m_fid, OP_STRING_ITOS); }
|
||||
|
@ -215,8 +224,8 @@ public:
|
|||
MATCH_TERNARY(is_extract);
|
||||
MATCH_BINARY(is_contains);
|
||||
MATCH_BINARY(is_at);
|
||||
MATCH_BINARY(is_stridof);
|
||||
MATCH_BINARY(is_repl);
|
||||
MATCH_TERNARY(is_index);
|
||||
MATCH_TERNARY(is_replace);
|
||||
MATCH_BINARY(is_prefix);
|
||||
MATCH_BINARY(is_suffix);
|
||||
MATCH_UNARY(is_itos);
|
||||
|
@ -234,6 +243,15 @@ public:
|
|||
public:
|
||||
re(seq_util& u): m(u.m), m_fid(u.m_fid) {}
|
||||
|
||||
app* mk_to_re(expr* s) { return m.mk_app(m_fid, OP_SEQ_TO_RE, 1, &s); }
|
||||
app* mk_in_re(expr* s, expr* r) { return m.mk_app(m_fid, OP_SEQ_IN_RE, s, r); }
|
||||
app* mk_concat(expr* r1, expr* r2) { return m.mk_app(m_fid, OP_RE_CONCAT, r1, r2); }
|
||||
app* mk_union(expr* r1, expr* r2) { return m.mk_app(m_fid, OP_RE_UNION, r1, r2); }
|
||||
app* mk_inter(expr* r1, expr* r2) { return m.mk_app(m_fid, OP_RE_INTERSECT, r1, r2); }
|
||||
app* mk_star(expr* r) { return m.mk_app(m_fid, OP_RE_STAR, r); }
|
||||
app* mk_plus(expr* r) { return m.mk_app(m_fid, OP_RE_PLUS, r); }
|
||||
app* mk_opt(expr* r) { return m.mk_app(m_fid, OP_RE_OPTION, r); }
|
||||
|
||||
bool is_to_re(expr const* n) const { return is_app_of(n, m_fid, OP_SEQ_TO_RE); }
|
||||
bool is_concat(expr const* n) const { return is_app_of(n, m_fid, OP_RE_CONCAT); }
|
||||
bool is_union(expr const* n) const { return is_app_of(n, m_fid, OP_RE_UNION); }
|
||||
|
|
|
@ -222,8 +222,10 @@ ATOMIC_CMD(reset_assertions_cmd, "reset-assertions", "reset all asserted formula
|
|||
UNARY_CMD(set_logic_cmd, "set-logic", "<symbol>", "set the background logic.", CPK_SYMBOL, symbol const &,
|
||||
if (ctx.set_logic(arg))
|
||||
ctx.print_success();
|
||||
else
|
||||
ctx.print_unsupported(symbol::null, m_line, m_pos);
|
||||
else {
|
||||
std::string msg = "ignoring unsupported logic " + arg.str();
|
||||
ctx.print_unsupported(symbol(msg.c_str()), m_line, m_pos);
|
||||
}
|
||||
);
|
||||
|
||||
UNARY_CMD(pp_cmd, "display", "<term>", "display the given term.", CPK_EXPR, expr *, {
|
||||
|
|
|
@ -20,6 +20,7 @@ Revision History:
|
|||
#include"arith_decl_plugin.h"
|
||||
#include"array_decl_plugin.h"
|
||||
#include"bv_decl_plugin.h"
|
||||
#include"seq_decl_plugin.h"
|
||||
#include"ast_pp.h"
|
||||
#include"for_each_expr.h"
|
||||
|
||||
|
@ -29,6 +30,7 @@ struct check_logic::imp {
|
|||
arith_util m_a_util;
|
||||
bv_util m_bv_util;
|
||||
array_util m_ar_util;
|
||||
seq_util m_seq_util;
|
||||
bool m_uf; // true if the logic supports uninterpreted functions
|
||||
bool m_arrays; // true if the logic supports arbitrary arrays
|
||||
bool m_bv_arrays; // true if the logic supports only bv arrays
|
||||
|
@ -40,7 +42,7 @@ struct check_logic::imp {
|
|||
bool m_quantifiers; // true if the logic supports quantifiers
|
||||
bool m_unknown_logic;
|
||||
|
||||
imp(ast_manager & _m):m(_m), m_a_util(m), m_bv_util(m), m_ar_util(m) {
|
||||
imp(ast_manager & _m):m(_m), m_a_util(m), m_bv_util(m), m_ar_util(m), m_seq_util(m) {
|
||||
reset();
|
||||
}
|
||||
|
||||
|
@ -168,6 +170,14 @@ struct check_logic::imp {
|
|||
m_bvs = true;
|
||||
m_quantifiers = true;
|
||||
}
|
||||
else if (logic == "QF_S") {
|
||||
m_uf = true;
|
||||
m_bvs = true;
|
||||
m_ints = true;
|
||||
m_arrays = true;
|
||||
m_reals = true;
|
||||
m_quantifiers = false;
|
||||
}
|
||||
else {
|
||||
m_unknown_logic = true;
|
||||
}
|
||||
|
@ -419,6 +429,9 @@ struct check_logic::imp {
|
|||
else if (m.is_builtin_family_id(fid)) {
|
||||
// nothing to check
|
||||
}
|
||||
else if (fid == m_seq_util.get_family_id()) {
|
||||
// nothing to check
|
||||
}
|
||||
else {
|
||||
fail("logic does not support theory");
|
||||
}
|
||||
|
|
|
@ -544,6 +544,7 @@ bool cmd_context::logic_has_arith_core(symbol const & s) const {
|
|||
s == "QF_FP" ||
|
||||
s == "QF_FPBV" ||
|
||||
s == "QF_BVFP" ||
|
||||
s == "QF_S" ||
|
||||
s == "HORN";
|
||||
}
|
||||
|
||||
|
@ -576,7 +577,7 @@ bool cmd_context::logic_has_bv() const {
|
|||
}
|
||||
|
||||
bool cmd_context::logic_has_seq_core(symbol const& s) const {
|
||||
return s == "QF_BVRE";
|
||||
return s == "QF_BVRE" || s == "QF_S";
|
||||
}
|
||||
|
||||
bool cmd_context::logic_has_seq() const {
|
||||
|
@ -712,13 +713,7 @@ bool cmd_context::set_logic(symbol const & s) {
|
|||
if (has_manager() && m_main_ctx)
|
||||
throw cmd_exception("logic must be set before initialization");
|
||||
if (!supported_logic(s)) {
|
||||
if (m_params.m_smtlib2_compliant) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
warning_msg("unknown logic, ignoring set-logic command");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
m_logic = s;
|
||||
if (is_logic("QF_RDL") ||
|
||||
|
|
|
@ -196,6 +196,7 @@ bool proto_model::eval(expr * e, expr_ref & result, bool model_completion) {
|
|||
ptr_buffer<expr> args;
|
||||
expr * null = static_cast<expr*>(0);
|
||||
todo.push_back(std::make_pair(e, null));
|
||||
|
||||
|
||||
expr * a;
|
||||
expr * expanded_a;
|
||||
|
@ -254,6 +255,7 @@ bool proto_model::eval(expr * e, expr_ref & result, bool model_completion) {
|
|||
if (new_t.get() == 0) {
|
||||
// t is interpreted or model completion is disabled.
|
||||
m_simplifier.mk_app(f, num_args, args.c_ptr(), new_t);
|
||||
TRACE("model_eval", tout << mk_pp(t, m_manager) << " -> " << new_t << "\n";);
|
||||
trail.push_back(new_t);
|
||||
if (!is_app(new_t) || to_app(new_t)->get_decl() != f) {
|
||||
// if the result is not of the form (f ...), then assume we must simplify it.
|
||||
|
@ -302,6 +304,7 @@ bool proto_model::eval(expr * e, expr_ref & result, bool model_completion) {
|
|||
else {
|
||||
SASSERT(r1);
|
||||
trail.push_back(r1);
|
||||
TRACE("model_eval", tout << mk_pp(a, m_manager) << "\nevaluates to: " << r1 << "\n";);
|
||||
expr * r2 = 0;
|
||||
if (!eval_cache.find(r1.get(), r2)) {
|
||||
todo.back().second = r1;
|
||||
|
|
|
@ -312,6 +312,8 @@ namespace smt {
|
|||
d.m_phase_available = true;
|
||||
d.m_phase = !l.sign();
|
||||
TRACE("phase_selection", tout << "saving phase, is_pos: " << d.m_phase << " l: " << l << "\n";);
|
||||
TRACE("relevancy",
|
||||
tout << "is_atom: " << d.is_atom() << " is relevant: " << is_relevant_core(bool_var2expr(l.var())) << "\n";);
|
||||
if (d.is_atom() && (m_fparams.m_relevancy_lvl == 0 || (m_fparams.m_relevancy_lvl == 1 && !d.is_quantifier()) || is_relevant_core(bool_var2expr(l.var()))))
|
||||
m_atom_propagation_queue.push_back(l);
|
||||
|
||||
|
@ -805,8 +807,10 @@ namespace smt {
|
|||
void context::merge_theory_vars(enode * n2, enode * n1, eq_justification js) {
|
||||
enode * r2 = n2->get_root();
|
||||
enode * r1 = n1->get_root();
|
||||
if (!r1->has_th_vars() && !r2->has_th_vars())
|
||||
if (!r1->has_th_vars() && !r2->has_th_vars()) {
|
||||
TRACE("merge_theory_vars", tout << "Neither have theory vars #" << n1->get_owner()->get_id() << " #" << n2->get_owner()->get_id() << "\n";);
|
||||
return;
|
||||
}
|
||||
|
||||
theory_id from_th = null_theory_id;
|
||||
|
||||
|
@ -821,7 +825,7 @@ namespace smt {
|
|||
theory_var v2 = m_fparams.m_new_core2th_eq ? get_closest_var(n2, t2) : r2->m_th_var_list.get_th_var();
|
||||
theory_var v1 = m_fparams.m_new_core2th_eq ? get_closest_var(n1, t1) : r1->m_th_var_list.get_th_var();
|
||||
TRACE("merge_theory_vars",
|
||||
tout << "v2: " << v2 << " #" << r1->get_owner_id() << ", v1: " << v1 << " #" << r2->get_owner_id()
|
||||
tout << "v2: " << v2 << " #" << r2->get_owner_id() << ", v1: " << v1 << " #" << r1->get_owner_id()
|
||||
<< ", t2: " << t2 << ", t1: " << t1 << "\n";);
|
||||
if (v2 != null_theory_var && v1 != null_theory_var) {
|
||||
if (t1 == t2) {
|
||||
|
|
|
@ -1744,7 +1744,7 @@ namespace smt {
|
|||
// when the quantifier is satisfied by a macro/hint, it may not be processed by the AUF solver.
|
||||
// in this case, the quantifier_info stores the instantiation sets.
|
||||
ptr_vector<instantiation_set> * m_uvar_inst_sets;
|
||||
bool m_cancel;
|
||||
volatile bool m_cancel;
|
||||
|
||||
friend class quantifier_analyzer;
|
||||
|
||||
|
@ -1893,8 +1893,10 @@ namespace smt {
|
|||
(*it)->populate_inst_sets(m_flat_q, s, ctx);
|
||||
// second pass
|
||||
it = m_qinfo_vect.begin();
|
||||
for (; it != end; ++it)
|
||||
for (; it != end; ++it) {
|
||||
checkpoint();
|
||||
(*it)->populate_inst_sets2(m_flat_q, s, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
func_decl_set const & get_ng_decls() const {
|
||||
|
@ -3593,6 +3595,10 @@ namespace smt {
|
|||
void model_finder::set_cancel(bool f) {
|
||||
m_cancel = f;
|
||||
m_analyzer->set_cancel(f);
|
||||
obj_map<quantifier, quantifier_info *>::iterator it = m_q2info.begin();
|
||||
obj_map<quantifier, quantifier_info *>::iterator end = m_q2info.end();
|
||||
for (; it != end; ++it)
|
||||
(*it).m_value->set_cancel(f);
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -815,7 +815,7 @@ namespace smt {
|
|||
}
|
||||
|
||||
void setup::setup_seq() {
|
||||
m_context.register_plugin(alloc(theory_seq, m_manager));
|
||||
m_context.register_plugin(alloc(theory_seq_empty, m_manager));
|
||||
}
|
||||
|
||||
void setup::setup_card() {
|
||||
|
|
|
@ -315,7 +315,7 @@ namespace smt {
|
|||
}
|
||||
|
||||
virtual void display(std::ostream & out) const {
|
||||
out << "Theory " << static_cast<int>(get_id()) << " does not have a display method\n";
|
||||
out << "Theory " << static_cast<int>(get_id()) << typeid(*this).name() << " does not have a display method\n";
|
||||
display_var2enode(out);
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ namespace smt {
|
|||
|
||||
template<typename Ext>
|
||||
void theory_arith<Ext>::display(std::ostream & out) const {
|
||||
if (get_num_vars() == 0) return;
|
||||
out << "Theory arithmetic:\n";
|
||||
display_vars(out);
|
||||
display_nl_monomials(out);
|
||||
|
|
|
@ -431,8 +431,9 @@ namespace smt {
|
|||
}
|
||||
|
||||
void theory_array::display(std::ostream & out) const {
|
||||
out << "Theory array:\n";
|
||||
unsigned num_vars = get_num_vars();
|
||||
if (num_vars == 0) return;
|
||||
out << "Theory array:\n";
|
||||
for (unsigned v = 0; v < num_vars; v++) {
|
||||
display_var(out, v);
|
||||
}
|
||||
|
|
|
@ -1586,8 +1586,9 @@ namespace smt {
|
|||
}
|
||||
|
||||
void theory_bv::display(std::ostream & out) const {
|
||||
out << "Theory bv:\n";
|
||||
unsigned num_vars = get_num_vars();
|
||||
if (num_vars == 0) return;
|
||||
out << "Theory bv:\n";
|
||||
for (unsigned v = 0; v < num_vars; v++) {
|
||||
display_var(out, v);
|
||||
}
|
||||
|
|
|
@ -522,8 +522,9 @@ namespace smt {
|
|||
}
|
||||
|
||||
void theory_datatype::display(std::ostream & out) const {
|
||||
out << "Theory datatype:\n";
|
||||
unsigned num_vars = get_num_vars();
|
||||
if (num_vars == 0) return;
|
||||
out << "Theory datatype:\n";
|
||||
for (unsigned v = 0; v < num_vars; v++)
|
||||
display_var(out, v);
|
||||
}
|
||||
|
|
|
@ -194,6 +194,9 @@ namespace smt {
|
|||
}
|
||||
}
|
||||
|
||||
virtual void display(std::ostream & out) const {
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -921,14 +921,20 @@ namespace smt {
|
|||
ast_manager & m = get_manager();
|
||||
context & ctx = get_context();
|
||||
|
||||
out << "fpa theory variables:" << std::endl;
|
||||
bool first = true;
|
||||
ptr_vector<enode>::const_iterator it = ctx.begin_enodes();
|
||||
ptr_vector<enode>::const_iterator end = ctx.end_enodes();
|
||||
for (; it != end; it++) {
|
||||
theory_var v = (*it)->get_th_var(get_family_id());
|
||||
if (v != -1) out << v << " -> " <<
|
||||
mk_ismt2_pp((*it)->get_owner(), m) << std::endl;
|
||||
if (v != -1) {
|
||||
if (first) out << "fpa theory variables:" << std::endl;
|
||||
out << v << " -> " <<
|
||||
mk_ismt2_pp((*it)->get_owner(), m) << std::endl;
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
// if there are no fpa theory variables, was fp ever used?
|
||||
if (first) return;
|
||||
|
||||
out << "bv theory variables:" << std::endl;
|
||||
it = ctx.begin_enodes();
|
||||
|
|
|
@ -22,24 +22,26 @@ Revision History:
|
|||
#include "smt_model_generator.h"
|
||||
#include "theory_seq.h"
|
||||
#include "seq_rewriter.h"
|
||||
#include "ast_trail.h"
|
||||
|
||||
using namespace smt;
|
||||
|
||||
void theory_seq::solution_map::update(expr* e, expr* r, enode_pair_dependency* d) {
|
||||
std::pair<expr*, enode_pair_dependency*> value;
|
||||
if (m_map.find(e, value)) {
|
||||
m_updates.push_back(DEL);
|
||||
m_lhs.push_back(e);
|
||||
m_rhs.push_back(value.first);
|
||||
m_deps.push_back(value.second);
|
||||
add_trail(DEL, e, value.first, value.second);
|
||||
}
|
||||
value.first = r;
|
||||
value.second = d;
|
||||
m_map.insert(e, value);
|
||||
m_updates.push_back(INS);
|
||||
m_lhs.push_back(e);
|
||||
m_rhs.push_back(value.first);
|
||||
m_deps.push_back(value.second);
|
||||
add_trail(INS, e, r, d);
|
||||
}
|
||||
|
||||
void theory_seq::solution_map::add_trail(map_update op, expr* l, expr* r, enode_pair_dependency* d) {
|
||||
m_updates.push_back(op);
|
||||
m_lhs.push_back(l);
|
||||
m_rhs.push_back(r);
|
||||
m_deps.push_back(d);
|
||||
}
|
||||
|
||||
expr* theory_seq::solution_map::find(expr* e, enode_pair_dependency*& d) {
|
||||
|
@ -84,29 +86,62 @@ void theory_seq::solution_map::display(std::ostream& out) const {
|
|||
}
|
||||
}
|
||||
|
||||
void theory_seq::exclusion_table::update(expr* e, expr* r) {
|
||||
if (e->get_id() > r->get_id()) {
|
||||
std::swap(e, r);
|
||||
}
|
||||
if (e != r && !m_table.contains(std::make_pair(e, r))) {
|
||||
m_lhs.push_back(e);
|
||||
m_rhs.push_back(r);
|
||||
m_table.insert(std::make_pair(e, r));
|
||||
}
|
||||
}
|
||||
|
||||
void theory_seq::exclusion_table::pop_scope(unsigned num_scopes) {
|
||||
if (num_scopes == 0) return;
|
||||
unsigned start = m_limit[m_limit.size() - num_scopes];
|
||||
for (unsigned i = start; i < m_lhs.size(); ++i) {
|
||||
m_table.erase(std::make_pair(m_lhs[i].get(), m_rhs[i].get()));
|
||||
}
|
||||
m_lhs.resize(start);
|
||||
m_rhs.resize(start);
|
||||
m_limit.resize(m_limit.size() - num_scopes);
|
||||
}
|
||||
|
||||
void theory_seq::exclusion_table::display(std::ostream& out) const {
|
||||
table_t::iterator it = m_table.begin(), end = m_table.end();
|
||||
for (; it != end; ++it) {
|
||||
out << mk_pp(it->first, m) << " != " << mk_pp(it->second, m) << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
theory_seq::theory_seq(ast_manager& m):
|
||||
theory(m.mk_family_id("seq")),
|
||||
m(m),
|
||||
m_dam(m_dep_array_value_manager, m_alloc),
|
||||
m_rep(m, m_dm),
|
||||
m_rep(m, m_dm),
|
||||
m_sort2len_fn(m),
|
||||
m_factory(0),
|
||||
m_ineqs(m),
|
||||
m_exclude(m),
|
||||
m_axioms(m),
|
||||
m_axioms_head(0),
|
||||
m_branch_variable_head(0),
|
||||
m_incomplete(false),
|
||||
m_model_completion(false),
|
||||
m_rewrite(m),
|
||||
m_util(m),
|
||||
m_autil(m),
|
||||
m_trail_stack(*this) {
|
||||
m_trail_stack(*this) {
|
||||
m_lhs.push_back(expr_array());
|
||||
m_rhs.push_back(expr_array());
|
||||
m_deps.push_back(enode_pair_dependency_array());
|
||||
m_prefix_sym = "prefix";
|
||||
m_suffix_sym = "suffix";
|
||||
m_left_sym = "left";
|
||||
m_right_sym = "right";
|
||||
m_contains_left_sym = "contains_left";
|
||||
m_contains_right_sym = "contains_right";
|
||||
m_prefix_sym = "seq.prefix.suffix";
|
||||
m_suffix_sym = "seq.suffix.prefix";
|
||||
m_left_sym = "seq.left";
|
||||
m_right_sym = "seq.right";
|
||||
m_contains_left_sym = "seq.contains.left";
|
||||
m_contains_right_sym = "seq.contains.right";
|
||||
}
|
||||
|
||||
theory_seq::~theory_seq() {
|
||||
|
@ -130,6 +165,15 @@ final_check_status theory_seq::final_check_eh() {
|
|||
if (ctx.inconsistent()) {
|
||||
return FC_CONTINUE;
|
||||
}
|
||||
if (branch_variable()) {
|
||||
return FC_CONTINUE;
|
||||
}
|
||||
if (split_variable()) {
|
||||
return FC_CONTINUE;
|
||||
}
|
||||
if (ctx.inconsistent()) {
|
||||
return FC_CONTINUE;
|
||||
}
|
||||
if (m.size(m_lhs.back()) > 0 || m_incomplete) {
|
||||
return FC_GIVEUP;
|
||||
}
|
||||
|
@ -152,19 +196,98 @@ bool theory_seq::check_ineqs() {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool theory_seq::branch_variable() {
|
||||
context& ctx = get_context();
|
||||
TRACE("seq", ctx.display(tout););
|
||||
expr_array& lhs = m_lhs.back();
|
||||
expr_array& rhs = m_rhs.back();
|
||||
unsigned sz = m.size(lhs);
|
||||
ptr_vector<expr> ls, rs;
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
unsigned k = (i + m_branch_variable_head) % sz;
|
||||
expr* l = m.get(lhs, k);
|
||||
expr* r = m.get(rhs, k);
|
||||
TRACE("seq", tout << mk_pp(l, m) << " = " << mk_pp(r, m) << "\n";);
|
||||
ls.reset(); rs.reset();
|
||||
m_util.str.get_concat(l, ls);
|
||||
m_util.str.get_concat(r, rs);
|
||||
|
||||
if (!ls.empty() && find_branch_candidate(ls[0], rs)) {
|
||||
m_branch_variable_head = k;
|
||||
return true;
|
||||
}
|
||||
if (!rs.empty() && find_branch_candidate(rs[0], ls)) {
|
||||
m_branch_variable_head = k;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool theory_seq::find_branch_candidate(expr* l, ptr_vector<expr> const& rs) {
|
||||
|
||||
TRACE("seq", tout << mk_pp(l, m) << " "
|
||||
<< (is_var(l)?"var":"not var") << "\n";);
|
||||
|
||||
if (!is_var(l)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
expr_ref v0(m), v(m);
|
||||
v0 = m_util.str.mk_empty(m.get_sort(l));
|
||||
if (assume_equality(l, v0)) {
|
||||
return true;
|
||||
}
|
||||
for (unsigned j = 0; j < rs.size(); ++j) {
|
||||
if (occurs(l, rs[j])) {
|
||||
return false;
|
||||
}
|
||||
std::string s;
|
||||
if (m_util.str.is_string(rs[j], s)) {
|
||||
for (size_t k = 1; k < s.length(); ++k) {
|
||||
v = m_util.str.mk_string(std::string(s.c_str(), k));
|
||||
if (v0) v = m_util.str.mk_concat(v0, v);
|
||||
if (assume_equality(l, v)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
v0 = (j == 0)? rs[0] : m_util.str.mk_concat(v0, rs[j]);
|
||||
if (assume_equality(l, v0)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool theory_seq::assume_equality(expr* l, expr* r) {
|
||||
context& ctx = get_context();
|
||||
if (m_exclude.contains(l, r)) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
TRACE("seq", tout << mk_pp(l, m) << " = " << mk_pp(r, m) << "\n";);
|
||||
if (!ctx.e_internalized(l)) ctx.internalize(l, false);
|
||||
if (!ctx.e_internalized(r)) ctx.internalize(r, false);
|
||||
ctx.mark_as_relevant(ctx.get_enode(l));
|
||||
ctx.mark_as_relevant(ctx.get_enode(r));
|
||||
ctx.assume_eq(ctx.get_enode(l), ctx.get_enode(r));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool theory_seq::split_variable() {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void theory_seq::propagate_lit(enode_pair_dependency* dep, literal lit) {
|
||||
context& ctx = get_context();
|
||||
ctx.mark_as_relevant(lit);
|
||||
vector<enode_pair, false> _eqs;
|
||||
m_dm.linearize(dep, _eqs);
|
||||
TRACE("seq",
|
||||
ctx.display_detailed_literal(tout, lit);
|
||||
tout << " <- ";
|
||||
for (unsigned i = 0; i < _eqs.size(); ++i) {
|
||||
tout << mk_pp(_eqs[i].first->get_owner(), m) << " = "
|
||||
<< mk_pp(_eqs[i].second->get_owner(), m) << "\n";
|
||||
}
|
||||
);
|
||||
TRACE("seq", ctx.display_detailed_literal(tout, lit);
|
||||
tout << " <-\n"; display_deps(tout, dep););
|
||||
justification* js =
|
||||
ctx.mk_justification(
|
||||
ext_theory_propagation_justification(
|
||||
|
@ -177,12 +300,7 @@ void theory_seq::set_conflict(enode_pair_dependency* dep) {
|
|||
context& ctx = get_context();
|
||||
vector<enode_pair, false> _eqs;
|
||||
m_dm.linearize(dep, _eqs);
|
||||
TRACE("seq",
|
||||
for (unsigned i = 0; i < _eqs.size(); ++i) {
|
||||
tout << mk_pp(_eqs[i].first->get_owner(), m) << " = "
|
||||
<< mk_pp(_eqs[i].second->get_owner(), m) << "\n";
|
||||
}
|
||||
);
|
||||
TRACE("seq", display_deps(tout, dep););
|
||||
ctx.set_conflict(
|
||||
ctx.mk_justification(
|
||||
ext_theory_conflict_justification(
|
||||
|
@ -195,10 +313,7 @@ void theory_seq::propagate_eq(enode_pair_dependency* dep, enode* n1, enode* n2)
|
|||
m_dm.linearize(dep, _eqs);
|
||||
TRACE("seq",
|
||||
tout << mk_pp(n1->get_owner(), m) << " " << mk_pp(n2->get_owner(), m) << " <- ";
|
||||
for (unsigned i = 0; i < _eqs.size(); ++i) {
|
||||
tout << mk_pp(_eqs[i].first->get_owner(), m) << " = "
|
||||
<< mk_pp(_eqs[i].second->get_owner(), m) << "\n";
|
||||
}
|
||||
display_deps(tout, dep);
|
||||
);
|
||||
|
||||
justification* js = ctx.mk_justification(
|
||||
|
@ -283,7 +398,12 @@ bool theory_seq::occurs(expr* a, expr* b) {
|
|||
}
|
||||
|
||||
bool theory_seq::is_var(expr* a) {
|
||||
return is_uninterp(a) || m_util.is_skolem(a);
|
||||
return
|
||||
m_util.is_seq(a) &&
|
||||
!m_util.str.is_concat(a) &&
|
||||
!m_util.str.is_empty(a) &&
|
||||
!m_util.str.is_string(a) &&
|
||||
!m_util.str.is_unit(a);
|
||||
}
|
||||
|
||||
bool theory_seq::is_left_select(expr* a, expr*& b) {
|
||||
|
@ -296,15 +416,12 @@ bool theory_seq::is_right_select(expr* a, expr*& b) {
|
|||
to_app(a)->get_decl()->get_parameter(0).get_symbol() == m_right_sym && (b = to_app(a)->get_arg(0), true);
|
||||
}
|
||||
|
||||
|
||||
void theory_seq::add_solution(expr* l, expr* r, enode_pair_dependency* deps) {
|
||||
context& ctx = get_context();
|
||||
m_rep.update(l, r, deps);
|
||||
// TBD: skip new equalities for non-internalized terms.
|
||||
if (ctx.e_internalized(l) && ctx.e_internalized(r)) {
|
||||
enode* n1 = ctx.get_enode(l);
|
||||
enode* n2 = ctx.get_enode(r);
|
||||
propagate_eq(deps, n1, n2);
|
||||
propagate_eq(deps, ctx.get_enode(l), ctx.get_enode(r));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -352,13 +469,7 @@ bool theory_seq::simplify_and_solve_eqs() {
|
|||
return change;
|
||||
}
|
||||
|
||||
|
||||
final_check_status theory_seq::add_axioms() {
|
||||
for (unsigned i = 0; i < get_num_vars(); ++i) {
|
||||
|
||||
// add axioms for len(x) when x = a ++ b
|
||||
}
|
||||
return FC_DONE;
|
||||
void theory_seq::internalize_eq_eh(app * atom, bool_var v) {
|
||||
}
|
||||
|
||||
bool theory_seq::internalize_atom(app* a, bool) {
|
||||
|
@ -366,23 +477,29 @@ bool theory_seq::internalize_atom(app* a, bool) {
|
|||
}
|
||||
|
||||
bool theory_seq::internalize_term(app* term) {
|
||||
TRACE("seq", tout << mk_pp(term, m) << "\n";);
|
||||
context & ctx = get_context();
|
||||
unsigned num_args = term->get_num_args();
|
||||
for (unsigned i = 0; i < num_args; i++) {
|
||||
ctx.internalize(term->get_arg(i), false);
|
||||
}
|
||||
if (ctx.e_internalized(term)) {
|
||||
return true;
|
||||
expr* arg = term->get_arg(i);
|
||||
ctx.internalize(arg, false);
|
||||
if (ctx.e_internalized(arg)) {
|
||||
mk_var(ctx.get_enode(arg));
|
||||
}
|
||||
}
|
||||
if (m.is_bool(term)) {
|
||||
bool_var bv = ctx.mk_bool_var(term);
|
||||
ctx.set_var_theory(bv, get_id());
|
||||
ctx.set_enode_flag(bv, true);
|
||||
}
|
||||
else {
|
||||
enode * e = ctx.mk_enode(term, false, m.is_bool(term), true);
|
||||
theory_var v = mk_var(e);
|
||||
ctx.attach_th_var(e, this, v);
|
||||
enode* e = 0;
|
||||
if (ctx.e_internalized(term)) {
|
||||
e = ctx.get_enode(term);
|
||||
}
|
||||
else {
|
||||
e = ctx.mk_enode(term, false, m.is_bool(term), true);
|
||||
}
|
||||
mk_var(e);
|
||||
}
|
||||
if (!m_util.str.is_concat(term) &&
|
||||
!m_util.str.is_string(term) &&
|
||||
|
@ -394,39 +511,63 @@ bool theory_seq::internalize_term(app* term) {
|
|||
!m_util.is_skolem(term)) {
|
||||
set_incomplete(term);
|
||||
}
|
||||
|
||||
// assert basic axioms
|
||||
expr* arg;
|
||||
func_decl* fn;
|
||||
if (m_util.str.is_length(term, arg) && !m_sort2len_fn.find(m.get_sort(arg), fn)) {
|
||||
m_trail_stack.push(ast2ast_trail<theory_seq, sort, func_decl>(m_sort2len_fn, m.get_sort(arg), term->get_decl()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void theory_seq::apply_sort_cnstr(enode* n, sort* s) {
|
||||
if (!is_attached_to_var(n)) {
|
||||
mk_var(n);
|
||||
}
|
||||
mk_var(n);
|
||||
}
|
||||
|
||||
void theory_seq::display(std::ostream & out) const {
|
||||
if (m.size(m_lhs.back()) == 0 &&
|
||||
m_ineqs.empty() &&
|
||||
m_rep.empty() &&
|
||||
m_exclude.empty()) {
|
||||
return;
|
||||
}
|
||||
out << "Theory seq\n";
|
||||
if (m.size(m_lhs.back()) > 0) {
|
||||
out << "Equations:\n";
|
||||
display_equations(out);
|
||||
}
|
||||
if (!m_ineqs.empty()) {
|
||||
out << "Negative constraints:\n";
|
||||
for (unsigned i = 0; i < m_ineqs.size(); ++i) {
|
||||
out << mk_pp(m_ineqs[i], m) << "\n";
|
||||
}
|
||||
}
|
||||
if (!m_rep.empty()) {
|
||||
out << "Solved equations:\n";
|
||||
m_rep.display(out);
|
||||
}
|
||||
if (!m_exclude.empty()) {
|
||||
out << "Exclusions:\n";
|
||||
m_exclude.display(out);
|
||||
}
|
||||
}
|
||||
|
||||
void theory_seq::display_equations(std::ostream& out) const {
|
||||
expr_array const& lhs = m_lhs.back();
|
||||
expr_array const& rhs = m_rhs.back();
|
||||
enode_pair_dependency_array const& deps = m_deps.back();
|
||||
out << "Equations:\n";
|
||||
for (unsigned i = 0; i < m.size(lhs); ++i) {
|
||||
out << mk_pp(m.get(lhs, i), m) << " = " << mk_pp(m.get(rhs, i), m) << " <-\n";
|
||||
enode_pair_dependency* dep = m_dam.get(deps, i);
|
||||
if (dep) {
|
||||
vector<enode_pair, false> _eqs;
|
||||
const_cast<enode_pair_dependency_manager&>(m_dm).linearize(dep, _eqs);
|
||||
for (unsigned i = 0; i < _eqs.size(); ++i) {
|
||||
out << " " << mk_pp(_eqs[i].first->get_owner(), m) << " = " << mk_pp(_eqs[i].second->get_owner(), m) << "\n";
|
||||
}
|
||||
}
|
||||
display_deps(out, m_dam.get(deps, i));
|
||||
}
|
||||
}
|
||||
|
||||
void theory_seq::display_deps(std::ostream& out, enode_pair_dependency* dep) const {
|
||||
if (!dep) return;
|
||||
vector<enode_pair, false> _eqs;
|
||||
const_cast<enode_pair_dependency_manager&>(m_dm).linearize(dep, _eqs);
|
||||
for (unsigned i = 0; i < _eqs.size(); ++i) {
|
||||
out << " " << mk_pp(_eqs[i].first->get_owner(), m) << " = " << mk_pp(_eqs[i].second->get_owner(), m) << "\n";
|
||||
}
|
||||
out << "Negative constraints:\n";
|
||||
for (unsigned i = 0; i < m_ineqs.size(); ++i) {
|
||||
out << mk_pp(m_ineqs[i], m) << "\n";
|
||||
}
|
||||
out << "Solved equations:\n";
|
||||
m_rep.display(out);
|
||||
}
|
||||
|
||||
void theory_seq::collect_statistics(::statistics & st) const {
|
||||
|
@ -435,34 +576,15 @@ void theory_seq::collect_statistics(::statistics & st) const {
|
|||
}
|
||||
|
||||
void theory_seq::init_model(model_generator & mg) {
|
||||
m_factory = alloc(seq_factory, get_manager(),
|
||||
get_family_id(), mg.get_model());
|
||||
m_factory = alloc(seq_factory, get_manager(), get_family_id(), mg.get_model());
|
||||
mg.register_factory(m_factory);
|
||||
// TBD: this is still unsound model generation.
|
||||
// disequalities are not guaranteed. we need to
|
||||
// prime the factory with a prefix that cannot be
|
||||
// constructed using any existing combinations of the
|
||||
// strings (or units) that are used.
|
||||
for (unsigned i = 0; i < get_num_vars(); ++i) {
|
||||
expr* e = get_enode(i)->get_owner();
|
||||
if (m_util.is_seq(e)) {
|
||||
enode_pair_dependency* deps = 0;
|
||||
e = m_rep.find(e, deps);
|
||||
if (is_var(e)) {
|
||||
expr* val = m_factory->get_fresh_value(m.get_sort(e));
|
||||
m_rep.update(e, val, 0);
|
||||
}
|
||||
}
|
||||
else if (m_util.is_re(e)) {
|
||||
// TBD
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
model_value_proc * theory_seq::mk_value(enode * n, model_generator & mg) {
|
||||
enode_pair_dependency* deps = 0;
|
||||
expr_ref e(n->get_owner(), m);
|
||||
canonize(e, deps);
|
||||
flet<bool> _model_completion(m_model_completion, true);
|
||||
e = canonize(e, deps);
|
||||
SASSERT(is_app(e));
|
||||
m_factory->add_trail(e);
|
||||
return alloc(expr_wrapper_proc, to_app(e));
|
||||
|
@ -471,15 +593,27 @@ model_value_proc * theory_seq::mk_value(enode * n, model_generator & mg) {
|
|||
|
||||
|
||||
void theory_seq::set_incomplete(app* term) {
|
||||
TRACE("seq", tout << "No support for: " << mk_pp(term, m) << "\n";);
|
||||
if (!m_incomplete) {
|
||||
m_trail_stack.push(value_trail<theory_seq,bool>(m_incomplete));
|
||||
TRACE("seq", tout << "Incomplete operator: " << mk_pp(term, m) << "\n";);
|
||||
m_trail_stack.push(value_trail<theory_seq, bool>(m_incomplete));
|
||||
m_incomplete = true;
|
||||
}
|
||||
}
|
||||
|
||||
theory_var theory_seq::mk_var(enode* n) {
|
||||
return theory::mk_var(n);
|
||||
if (!m_util.is_seq(n->get_owner()) &&
|
||||
!m_util.is_re(n->get_owner())) {
|
||||
return null_theory_var;
|
||||
}
|
||||
if (is_attached_to_var(n)) {
|
||||
return n->get_th_var(get_id());
|
||||
}
|
||||
else {
|
||||
theory_var v = theory::mk_var(n);
|
||||
get_context().attach_th_var(n, this, v);
|
||||
get_context().mark_as_relevant(n);
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
bool theory_seq::can_propagate() {
|
||||
|
@ -515,6 +649,15 @@ expr_ref theory_seq::expand(expr* e, enode_pair_dependency*& eqs) {
|
|||
if (m_util.str.is_contains(e, e1, e2)) {
|
||||
return expr_ref(m_util.str.mk_contains(expand(e1, eqs), expand(e2, eqs)), m);
|
||||
}
|
||||
if (m_model_completion && is_var(e)) {
|
||||
SASSERT(m_factory);
|
||||
expr_ref val(m);
|
||||
val = m_factory->get_fresh_value(m.get_sort(e));
|
||||
if (val) {
|
||||
m_rep.update(e, val, 0);
|
||||
return val;
|
||||
}
|
||||
}
|
||||
return expr_ref(e, m);
|
||||
}
|
||||
|
||||
|
@ -530,29 +673,295 @@ void theory_seq::propagate() {
|
|||
while (m_axioms_head < m_axioms.size() && !ctx.inconsistent()) {
|
||||
expr_ref e(m);
|
||||
e = m_axioms[m_axioms_head].get();
|
||||
assert_axiom(e);
|
||||
deque_axiom(e);
|
||||
++m_axioms_head;
|
||||
}
|
||||
}
|
||||
|
||||
void theory_seq::create_axiom(expr_ref& e) {
|
||||
void theory_seq::enque_axiom(expr* e) {
|
||||
TRACE("seq", tout << "add axioms for: " << mk_pp(e, m) << "\n";);
|
||||
m_trail_stack.push(push_back_vector<theory_seq, expr_ref_vector>(m_axioms));
|
||||
m_axioms.push_back(e);
|
||||
}
|
||||
|
||||
void theory_seq::assert_axiom(expr_ref& e) {
|
||||
context & ctx = get_context();
|
||||
if (m.is_true(e)) return;
|
||||
TRACE("seq", tout << "asserting " << e << "\n";);
|
||||
ctx.internalize(e, false);
|
||||
literal lit(ctx.get_literal(e));
|
||||
ctx.mark_as_relevant(lit);
|
||||
ctx.mk_th_axiom(get_id(), 1, &lit);
|
||||
void theory_seq::deque_axiom(expr* n) {
|
||||
if (m_util.str.is_length(n)) {
|
||||
add_length_axiom(n);
|
||||
}
|
||||
else if (m_util.str.is_index(n)) {
|
||||
add_indexof_axiom(n);
|
||||
}
|
||||
else if (m_util.str.is_replace(n)) {
|
||||
add_replace_axiom(n);
|
||||
}
|
||||
else if (m_util.str.is_extract(n)) {
|
||||
add_extract_axiom(n);
|
||||
}
|
||||
else if (m_util.str.is_at(n)) {
|
||||
add_at_axiom(n);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
\brief nodes n1 and n2 are about to get merged.
|
||||
if n1 occurs in the context of a length application,
|
||||
then instantiate length axioms for each concatenation in the class of n2.
|
||||
In this way we ensure that length respects concatenation.
|
||||
*/
|
||||
void theory_seq::new_eq_len_concat(enode* n1, enode* n2) {
|
||||
context& ctx = get_context();
|
||||
if (n1->get_root() == n2->get_root()) {
|
||||
return;
|
||||
}
|
||||
SASSERT(n1->get_root() != n2->get_root());
|
||||
if (!m_util.is_seq(n1->get_owner())) {
|
||||
return;
|
||||
}
|
||||
func_decl* f_len = 0;
|
||||
if (!m_sort2len_fn.find(m.get_sort(n1->get_owner()), f_len)) {
|
||||
return;
|
||||
}
|
||||
|
||||
enode* r1 = n1->get_root();
|
||||
enode_vector::const_iterator it = ctx.begin_enodes_of(f_len);
|
||||
enode_vector::const_iterator end = ctx.end_enodes_of(f_len);
|
||||
bool has_len = false;
|
||||
for (; !has_len && it != end; ++it) {
|
||||
has_len = ((*it)->get_root() == r1);
|
||||
}
|
||||
if (!has_len) {
|
||||
return;
|
||||
}
|
||||
enode* start2 = n2;
|
||||
do {
|
||||
expr* o = n2->get_owner();
|
||||
if (!is_var(o)) {
|
||||
expr_ref ln(m_util.str.mk_length(o), m);
|
||||
enque_axiom(ln);
|
||||
}
|
||||
n2 = n2->get_next();
|
||||
}
|
||||
while (n2 != start2);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
encode that s is not a proper prefix of xs1
|
||||
where s1 is all of s, except the last element.
|
||||
|
||||
lit or s = "" or s = s1*c
|
||||
lit or s = "" or len(c) = 1
|
||||
lit or s = "" or !prefix(s, x*s1)
|
||||
*/
|
||||
void theory_seq::tightest_prefix(expr* s, expr* x, literal lit) {
|
||||
expr_ref s1 = mk_skolem(symbol("seq.first"), s);
|
||||
expr_ref c = mk_skolem(symbol("seq.last"), s);
|
||||
expr_ref s1c(m_util.str.mk_concat(s1, c), m);
|
||||
expr_ref lc(m_util.str.mk_length(c), m);
|
||||
expr_ref one(m_autil.mk_int(1), m);
|
||||
expr_ref emp(m_util.str.mk_empty(m.get_sort(s)), m);
|
||||
literal s_eq_emp = mk_eq(s, emp, false);
|
||||
add_axiom(lit, s_eq_emp, mk_eq(s, s1c, false));
|
||||
add_axiom(lit, s_eq_emp, mk_eq(lc, one, false));
|
||||
add_axiom(lit, s_eq_emp, ~mk_literal(m_util.str.mk_contains(s, m_util.str.mk_concat(x, s1))));
|
||||
}
|
||||
|
||||
/*
|
||||
let i = Index(t, s, offset)
|
||||
|
||||
if offset = 0:
|
||||
(!contains(t, s) -> i = -1)
|
||||
(s = empty -> i = 0)
|
||||
(contains(t, s) & s != empty -> t = xsy)
|
||||
(contains(t, s) -> tightest_prefix(s, x))
|
||||
if 0 <= offset < len(t):
|
||||
t = zt' & len(z) == offset
|
||||
add above constraints with t'
|
||||
if offset >= len(t):
|
||||
i = -1
|
||||
if offset < 0:
|
||||
?
|
||||
|
||||
optional lemmas:
|
||||
(len(s) > len(t) -> i = -1)
|
||||
(len(s) <= len(t) -> i <= len(t)-len(s))
|
||||
*/
|
||||
void theory_seq::add_indexof_axiom(expr* i) {
|
||||
expr* s, *t, *offset;
|
||||
rational r;
|
||||
VERIFY(m_util.str.is_index(i, t, s, offset));
|
||||
expr_ref emp(m), minus_one(m), zero(m), xsy(m);
|
||||
minus_one = m_autil.mk_int(-1);
|
||||
zero = m_autil.mk_int(0);
|
||||
emp = m_util.str.mk_empty(m.get_sort(s));
|
||||
if (m_autil.is_numeral(offset, r) && r.is_zero()) {
|
||||
expr_ref x = mk_skolem(m_contains_left_sym, t, s);
|
||||
expr_ref y = mk_skolem(m_contains_right_sym, t, s);
|
||||
xsy = m_util.str.mk_concat(x,s,y);
|
||||
literal cnt = mk_literal(m_util.str.mk_contains(t, s));
|
||||
literal eq_empty = mk_eq(s, emp, false);
|
||||
add_axiom(cnt, mk_eq(i, minus_one, false));
|
||||
add_axiom(~eq_empty, mk_eq(i, zero, false));
|
||||
add_axiom(~cnt, eq_empty, mk_eq(t, xsy, false));
|
||||
tightest_prefix(s, x, ~cnt);
|
||||
}
|
||||
else {
|
||||
// TBD
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
let r = replace(a, s, t)
|
||||
|
||||
(contains(a, s) -> tightest_prefix(s,xs))
|
||||
(contains(a, s) -> r = xty & a = xsy) &
|
||||
(!contains(a, s) -> r = a)
|
||||
|
||||
*/
|
||||
void theory_seq::add_replace_axiom(expr* r) {
|
||||
expr* a, *s, *t;
|
||||
VERIFY(m_util.str.is_replace(r, a, s, t));
|
||||
expr_ref x = mk_skolem(m_contains_left_sym, a, s);
|
||||
expr_ref y = mk_skolem(m_contains_right_sym, a, s);
|
||||
expr_ref xty(m_util.str.mk_concat(x, t, y), m);
|
||||
expr_ref xsy(m_util.str.mk_concat(x, s, y), m);
|
||||
literal cnt = mk_literal(m_util.str.mk_contains(a ,s));
|
||||
add_axiom(cnt, mk_eq(r, a, false));
|
||||
add_axiom(~cnt, mk_eq(a, xsy, false));
|
||||
add_axiom(~cnt, mk_eq(r, xty, false));
|
||||
tightest_prefix(s, x, ~cnt);
|
||||
}
|
||||
|
||||
/*
|
||||
let n = len(x)
|
||||
|
||||
len(x) >= 0
|
||||
len(x) = 0 => x = ""
|
||||
x = "" => len(x) = 0
|
||||
len(x) = rewrite(len(x))
|
||||
*/
|
||||
void theory_seq::add_length_axiom(expr* n) {
|
||||
expr* x, *a, *b;
|
||||
VERIFY(m_util.str.is_length(n, x));
|
||||
expr_ref zero(m), one(m), emp(m);
|
||||
zero = m_autil.mk_int(0);
|
||||
std::string s;
|
||||
if (m_util.str.is_unit(n)) {
|
||||
one = m_autil.mk_int(1);
|
||||
add_axiom(mk_eq(n, one, false));
|
||||
}
|
||||
else if (m_util.str.is_empty(n)) {
|
||||
add_axiom(mk_eq(n, zero, false));
|
||||
}
|
||||
else if (m_util.str.is_string(n, s)) {
|
||||
expr_ref ls(m_autil.mk_numeral(rational(s.length(), rational::ui64()), true), m);
|
||||
add_axiom(mk_eq(n, ls, false));
|
||||
}
|
||||
else if (m_util.str.is_concat(n, a, b)) {
|
||||
expr_ref _a(m_util.str.mk_length(a), m);
|
||||
expr_ref _b(m_util.str.mk_length(b), m);
|
||||
expr_ref a_p_b(m_autil.mk_add(_a, _b), m);
|
||||
add_axiom(mk_eq(n, a_p_b, false));
|
||||
}
|
||||
else {
|
||||
emp = m_util.str.mk_empty(m.get_sort(x));
|
||||
literal eq1(mk_eq(zero, n, false));
|
||||
literal eq2(mk_eq(x, emp, false));
|
||||
add_axiom(mk_literal(m_autil.mk_ge(n, zero)));
|
||||
add_axiom(~eq1, eq2);
|
||||
add_axiom(~eq2, eq1);
|
||||
}
|
||||
}
|
||||
|
||||
expr* theory_seq::mk_sub(expr* a, expr* b) {
|
||||
return m_autil.mk_add(a, m_autil.mk_mul(m_autil.mk_int(-1), b));
|
||||
}
|
||||
|
||||
/*
|
||||
TBD: check semantics of extract.
|
||||
|
||||
let e = extract(s, i, l)
|
||||
|
||||
0 <= i < len(s) -> prefix(xe,s) & len(x) = i
|
||||
0 <= i < len(s) & l >= len(s) - i -> len(e) = len(s) - i
|
||||
0 <= i < len(s) & 0 <= l < len(s) - i -> len(e) = l
|
||||
0 <= i < len(s) & l < 0 -> len(e) = 0
|
||||
* i < 0 -> e = s
|
||||
* i >= len(s) -> e = empty
|
||||
*/
|
||||
|
||||
void theory_seq::add_extract_axiom(expr* e) {
|
||||
expr* s, *i, *l;
|
||||
VERIFY(m_util.str.is_extract(e, s, i, l));
|
||||
expr_ref x(mk_skolem(symbol("seq.extract.prefix"), s, e), m);
|
||||
expr_ref ls(m_util.str.mk_length(s), m);
|
||||
expr_ref lx(m_util.str.mk_length(x), m);
|
||||
expr_ref le(m_util.str.mk_length(e), m);
|
||||
expr_ref ls_minus_i(mk_sub(ls, i), m);
|
||||
expr_ref xe(m_util.str.mk_concat(x, e), m);
|
||||
expr_ref zero(m_autil.mk_int(0), m);
|
||||
|
||||
literal i_ge_0 = mk_literal(m_autil.mk_ge(i, zero));
|
||||
literal i_le_l = mk_literal(m_autil.mk_le(mk_sub(i, l), zero));
|
||||
literal i_ge_ls = mk_literal(m_autil.mk_ge(mk_sub(i, ls), zero));
|
||||
literal l_ge_ls = mk_literal(m_autil.mk_ge(mk_sub(l, ls), zero));
|
||||
literal l_ge_zero = mk_literal(m_autil.mk_ge(l, zero));
|
||||
|
||||
add_axiom(~i_ge_0, i_ge_ls, mk_literal(m_util.str.mk_prefix(xe, s)));
|
||||
add_axiom(~i_ge_0, i_ge_ls, mk_eq(lx, i, false));
|
||||
add_axiom(~i_ge_0, i_ge_ls, ~l_ge_ls, mk_eq(le, ls_minus_i, false));
|
||||
add_axiom(~i_ge_0, i_ge_ls, l_ge_zero, mk_eq(le, zero, false));
|
||||
}
|
||||
|
||||
/*
|
||||
let e = at(s, i)
|
||||
|
||||
0 <= i < len(s) -> s = xey & len(x) = i & len(e) = 1
|
||||
|
||||
*/
|
||||
void theory_seq::add_at_axiom(expr* e) {
|
||||
expr* s, *i;
|
||||
VERIFY(m_util.str.is_at(e, s, i));
|
||||
expr_ref x(m), y(m), lx(m), le(m), xey(m), zero(m), one(m), len_e(m), len_x(m);
|
||||
x = mk_skolem(symbol("seq.at.left"), s);
|
||||
y = mk_skolem(symbol("seq.at.right"), s);
|
||||
xey = m_util.str.mk_concat(x, e, y);
|
||||
zero = m_autil.mk_int(0);
|
||||
one = m_autil.mk_int(1);
|
||||
len_e = m_util.str.mk_length(e);
|
||||
len_x = m_util.str.mk_length(x);
|
||||
|
||||
literal i_ge_0 = mk_literal(m_autil.mk_ge(i, zero));
|
||||
literal i_ge_len_s = mk_literal(m_autil.mk_ge(mk_sub(i, m_util.str.mk_length(s)), zero));
|
||||
|
||||
add_axiom(~i_ge_0, i_ge_len_s, mk_eq(s, xey, false));
|
||||
add_axiom(~i_ge_0, i_ge_len_s, mk_eq(one, len_e, false));
|
||||
add_axiom(~i_ge_0, i_ge_len_s, mk_eq(i, len_x, false));
|
||||
}
|
||||
|
||||
|
||||
literal theory_seq::mk_literal(expr* _e) {
|
||||
expr_ref e(_e, m);
|
||||
context& ctx = get_context();
|
||||
ctx.internalize(e, false);
|
||||
return ctx.get_literal(e);
|
||||
}
|
||||
|
||||
void theory_seq::add_axiom(literal l1, literal l2, literal l3, literal l4) {
|
||||
context& ctx = get_context();
|
||||
literal_vector lits;
|
||||
if (l1 != null_literal) { ctx.mark_as_relevant(l1); lits.push_back(l1); }
|
||||
if (l2 != null_literal) { ctx.mark_as_relevant(l2); lits.push_back(l2); }
|
||||
if (l3 != null_literal) { ctx.mark_as_relevant(l3); lits.push_back(l3); }
|
||||
if (l4 != null_literal) { ctx.mark_as_relevant(l4); lits.push_back(l4); }
|
||||
TRACE("seq", ctx.display_literals_verbose(tout, lits.size(), lits.c_ptr()); tout << "\n";);
|
||||
ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr());
|
||||
}
|
||||
|
||||
|
||||
expr_ref theory_seq::mk_skolem(symbol const& name, expr* e1, expr* e2) {
|
||||
expr* es[2] = { e1, e2 };
|
||||
return expr_ref(m_util.mk_skolem(name, 2, es, m.get_sort(e1)), m);
|
||||
return expr_ref(m_util.mk_skolem(name, e2?2:1, es, m.get_sort(e1)), m);
|
||||
}
|
||||
|
||||
void theory_seq::propagate_eq(bool_var v, expr* e1, expr* e2) {
|
||||
|
@ -594,11 +1003,15 @@ void theory_seq::assign_eq(bool_var v, bool is_true) {
|
|||
else if (m_util.str.is_contains(e, e1, e2)) {
|
||||
expr_ref f1 = mk_skolem(m_contains_left_sym, e1, e2);
|
||||
expr_ref f2 = mk_skolem(m_contains_right_sym, e1, e2);
|
||||
f = m_util.str.mk_concat(m_util.str.mk_concat(f1, e1), f2);
|
||||
propagate_eq(v, f, e2);
|
||||
f = m_util.str.mk_concat(m_util.str.mk_concat(f1, e2), f2);
|
||||
propagate_eq(v, f, e1);
|
||||
}
|
||||
else if (m_util.str.is_in_re(e, e1, e2)) {
|
||||
NOT_IMPLEMENTED_YET();
|
||||
// TBD
|
||||
}
|
||||
else if (m.is_eq(e, e1, e2)) {
|
||||
new_eq_eh(ctx.get_enode(e1)->get_th_var(get_id()),
|
||||
ctx.get_enode(e1)->get_th_var(get_id()));
|
||||
}
|
||||
else {
|
||||
UNREACHABLE();
|
||||
|
@ -614,9 +1027,14 @@ void theory_seq::new_eq_eh(theory_var v1, theory_var v2) {
|
|||
enode* n1 = get_enode(v1);
|
||||
enode* n2 = get_enode(v2);
|
||||
if (n1 != n2) {
|
||||
m.push_back(m_lhs.back(), n1->get_owner());
|
||||
m.push_back(m_rhs.back(), n2->get_owner());
|
||||
expr* o1 = n1->get_owner(), *o2 = n2->get_owner();
|
||||
TRACE("seq", tout << mk_pp(o1, m) << " = " << mk_pp(o2, m) << "\n";);
|
||||
m.push_back(m_lhs.back(), o1);
|
||||
m.push_back(m_rhs.back(), o2);
|
||||
m_dam.push_back(m_deps.back(), m_dm.mk_leaf(enode_pair(n1, n2)));
|
||||
|
||||
new_eq_len_concat(n1, n2);
|
||||
new_eq_len_concat(n2, n1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -625,11 +1043,14 @@ void theory_seq::new_diseq_eh(theory_var v1, theory_var v2) {
|
|||
expr* e2 = get_enode(v2)->get_owner();
|
||||
m_trail_stack.push(push_back_vector<theory_seq, expr_ref_vector>(m_ineqs));
|
||||
m_ineqs.push_back(mk_eq_atom(e1, e2));
|
||||
m_exclude.update(e1, e2);
|
||||
}
|
||||
|
||||
void theory_seq::push_scope_eh() {
|
||||
TRACE("seq", tout << "push " << m_lhs.size() << "\n";);
|
||||
theory::push_scope_eh();
|
||||
m_rep.push_scope();
|
||||
m_exclude.push_scope();
|
||||
m_dm.push_scope();
|
||||
m_trail_stack.push_scope();
|
||||
m_trail_stack.push(value_trail<theory_seq, unsigned>(m_axioms_head));
|
||||
|
@ -644,10 +1065,12 @@ void theory_seq::push_scope_eh() {
|
|||
}
|
||||
|
||||
void theory_seq::pop_scope_eh(unsigned num_scopes) {
|
||||
TRACE("seq", tout << "pop " << m_lhs.size() << "\n";);
|
||||
m_trail_stack.pop_scope(num_scopes);
|
||||
theory::pop_scope_eh(num_scopes);
|
||||
m_dm.pop_scope(num_scopes);
|
||||
m_rep.pop_scope(num_scopes);
|
||||
m_exclude.pop_scope(num_scopes);
|
||||
while (num_scopes > 0) {
|
||||
--num_scopes;
|
||||
m.del(m_lhs.back());
|
||||
|
@ -673,10 +1096,12 @@ void theory_seq::restart_eh() {
|
|||
#endif
|
||||
}
|
||||
|
||||
void theory_seq::relevant_eh(app* n) {
|
||||
if (m_util.str.is_length(n)) {
|
||||
expr_ref e(m);
|
||||
e = m_autil.mk_le(m_autil.mk_numeral(rational(0), true), n);
|
||||
create_axiom(e);
|
||||
void theory_seq::relevant_eh(app* n) {
|
||||
if (m_util.str.is_length(n) ||
|
||||
m_util.str.is_index(n) ||
|
||||
m_util.str.is_replace(n) ||
|
||||
m_util.str.is_extract(n) ||
|
||||
m_util.str.is_at(n)) {
|
||||
enque_axiom(n);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ Revision History:
|
|||
#include "theory_seq_empty.h"
|
||||
#include "th_rewriter.h"
|
||||
#include "union_find.h"
|
||||
#include "ast_trail.h"
|
||||
|
||||
namespace smt {
|
||||
|
||||
|
@ -53,12 +54,15 @@ namespace smt {
|
|||
ast_manager& m;
|
||||
enode_pair_dependency_manager& m_dm;
|
||||
map_t m_map;
|
||||
expr_ref_vector m_lhs, m_rhs;
|
||||
expr_ref_vector m_lhs, m_rhs;
|
||||
ptr_vector<enode_pair_dependency> m_deps;
|
||||
svector<map_update> m_updates;
|
||||
unsigned_vector m_limit;
|
||||
|
||||
void add_trail(map_update op, expr* l, expr* r, enode_pair_dependency* d);
|
||||
public:
|
||||
solution_map(ast_manager& m, enode_pair_dependency_manager& dm): m(m), m_dm(dm), m_lhs(m), m_rhs(m) {}
|
||||
bool empty() const { return m_map.empty(); }
|
||||
void update(expr* e, expr* r, enode_pair_dependency* d);
|
||||
expr* find(expr* e, enode_pair_dependency*& d);
|
||||
void push_scope() { m_limit.push_back(m_updates.size()); }
|
||||
|
@ -66,6 +70,25 @@ namespace smt {
|
|||
void display(std::ostream& out) const;
|
||||
};
|
||||
|
||||
class exclusion_table {
|
||||
typedef obj_pair_hashtable<expr, expr> table_t;
|
||||
ast_manager& m;
|
||||
table_t m_table;
|
||||
expr_ref_vector m_lhs, m_rhs;
|
||||
unsigned_vector m_limit;
|
||||
public:
|
||||
exclusion_table(ast_manager& m): m(m), m_lhs(m), m_rhs(m) {}
|
||||
~exclusion_table() { }
|
||||
bool empty() const { return m_table.empty(); }
|
||||
void update(expr* e, expr* r);
|
||||
bool contains(expr* e, expr* r) {
|
||||
return m_table.contains(std::make_pair(e, r));
|
||||
}
|
||||
void push_scope() { m_limit.push_back(m_lhs.size()); }
|
||||
void pop_scope(unsigned num_scopes);
|
||||
void display(std::ostream& out) const;
|
||||
};
|
||||
|
||||
struct stats {
|
||||
stats() { reset(); }
|
||||
void reset() { memset(this, 0, sizeof(stats)); }
|
||||
|
@ -81,11 +104,15 @@ namespace smt {
|
|||
vector<expr_array> m_lhs, m_rhs; // persistent sets of equalities.
|
||||
vector<enode_pair_dependency_array> m_deps; // persistent sets of dependencies.
|
||||
|
||||
seq_factory* m_factory; // value factory
|
||||
expr_ref_vector m_ineqs; // inequalities to check
|
||||
expr_ref_vector m_axioms;
|
||||
unsigned m_axioms_head;
|
||||
bool m_incomplete;
|
||||
ast2ast_trailmap<sort, func_decl> m_sort2len_fn; // length functions per sort.
|
||||
seq_factory* m_factory; // value factory
|
||||
expr_ref_vector m_ineqs; // inequalities to check solution against
|
||||
exclusion_table m_exclude; // set of asserted disequalities.
|
||||
expr_ref_vector m_axioms; // list of axioms to add.
|
||||
unsigned m_axioms_head; // index of first axiom to add.
|
||||
unsigned m_branch_variable_head; // index of first equation to examine.
|
||||
bool m_incomplete; // is the solver (clearly) incomplete for the fragment.
|
||||
bool m_model_completion; // during model construction, invent values in canonizer
|
||||
th_rewriter m_rewrite;
|
||||
seq_util m_util;
|
||||
arith_util m_autil;
|
||||
|
@ -95,12 +122,13 @@ namespace smt {
|
|||
symbol m_suffix_sym;
|
||||
symbol m_contains_left_sym;
|
||||
symbol m_contains_right_sym;
|
||||
symbol m_left_sym;
|
||||
symbol m_right_sym;
|
||||
symbol m_left_sym; // split variable left part
|
||||
symbol m_right_sym; // split variable right part
|
||||
|
||||
virtual final_check_status final_check_eh();
|
||||
virtual bool internalize_atom(app*, bool);
|
||||
virtual bool internalize_term(app*);
|
||||
virtual void internalize_eq_eh(app * atom, bool_var v);
|
||||
virtual void new_eq_eh(theory_var, theory_var);
|
||||
virtual void new_diseq_eh(theory_var, theory_var);
|
||||
virtual void assign_eq(bool_var v, bool is_true);
|
||||
|
@ -114,40 +142,66 @@ namespace smt {
|
|||
virtual char const * get_name() const { return "seq"; }
|
||||
virtual theory_var mk_var(enode* n);
|
||||
virtual void apply_sort_cnstr(enode* n, sort* s);
|
||||
virtual void display(std::ostream & out) const;
|
||||
virtual void display(std::ostream & out) const;
|
||||
virtual void collect_statistics(::statistics & st) const;
|
||||
virtual model_value_proc * mk_value(enode * n, model_generator & mg);
|
||||
virtual void init_model(model_generator & mg);
|
||||
|
||||
bool check_ineqs();
|
||||
|
||||
// final check
|
||||
bool check_ineqs(); // check if inequalities are violated.
|
||||
bool simplify_and_solve_eqs(); // solve unitary equalities
|
||||
bool branch_variable(); // branch on a variable
|
||||
bool split_variable(); // split a variable
|
||||
|
||||
bool pre_process_eqs(bool simplify_or_solve);
|
||||
bool simplify_eqs();
|
||||
bool simplify_eq(expr* l, expr* r, enode_pair_dependency* dep);
|
||||
bool solve_unit_eq(expr* l, expr* r, enode_pair_dependency* dep);
|
||||
bool solve_basic_eqs();
|
||||
bool simplify_and_solve_eqs();
|
||||
|
||||
// asserting consequences
|
||||
void propagate_lit(enode_pair_dependency* dep, literal lit);
|
||||
void propagate_eq(enode_pair_dependency* dep, enode* n1, enode* n2);
|
||||
void propagate_eq(bool_var v, expr* e1, expr* e2);
|
||||
void set_conflict(enode_pair_dependency* dep);
|
||||
|
||||
bool find_branch_candidate(expr* l, ptr_vector<expr> const& rs);
|
||||
bool assume_equality(expr* l, expr* r);
|
||||
|
||||
// variable solving utilities
|
||||
bool occurs(expr* a, expr* b);
|
||||
bool is_var(expr* b);
|
||||
void add_solution(expr* l, expr* r, enode_pair_dependency* dep);
|
||||
bool is_left_select(expr* a, expr*& b);
|
||||
bool is_right_select(expr* a, expr*& b);
|
||||
|
||||
final_check_status add_axioms();
|
||||
|
||||
void assert_axiom(expr_ref& e);
|
||||
void create_axiom(expr_ref& e);
|
||||
expr_ref canonize(expr* e, enode_pair_dependency*& eqs);
|
||||
expr_ref expand(expr* e, enode_pair_dependency*& eqs);
|
||||
void add_dependency(enode_pair_dependency*& dep, enode* a, enode* b);
|
||||
|
||||
expr_ref mk_skolem(symbol const& s, expr* e1, expr* e2);
|
||||
|
||||
// terms whose meaning are encoded using axioms.
|
||||
void enque_axiom(expr* e);
|
||||
void deque_axiom(expr* e);
|
||||
void add_axiom(literal l1, literal l2 = null_literal, literal l3 = null_literal, literal l4 = null_literal);
|
||||
void add_indexof_axiom(expr* e);
|
||||
void add_replace_axiom(expr* e);
|
||||
void add_extract_axiom(expr* e);
|
||||
void add_length_axiom(expr* n);
|
||||
void add_at_axiom(expr* n);
|
||||
literal mk_literal(expr* n);
|
||||
void tightest_prefix(expr* s, expr* x, literal lit);
|
||||
expr* mk_sub(expr* a, expr* b);
|
||||
|
||||
void new_eq_len_concat(enode* n1, enode* n2);
|
||||
|
||||
|
||||
expr_ref mk_skolem(symbol const& s, expr* e1, expr* e2 = 0);
|
||||
|
||||
void set_incomplete(app* term);
|
||||
|
||||
// diagnostics
|
||||
void display_equations(std::ostream& out) const;
|
||||
void display_deps(std::ostream& out, enode_pair_dependency* deps) const;
|
||||
public:
|
||||
theory_seq(ast_manager& m);
|
||||
virtual ~theory_seq();
|
||||
|
|
|
@ -89,6 +89,11 @@ namespace smt {
|
|||
return u.str.mk_string(sym);
|
||||
}
|
||||
}
|
||||
sort* seq = 0;
|
||||
if (u.is_re(s, seq)) {
|
||||
expr* v0 = get_fresh_value(seq);
|
||||
return u.re.mk_to_re(v0);
|
||||
}
|
||||
NOT_IMPLEMENTED_YET();
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue