3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-07 18:05:21 +00:00

use ADT for strings

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2015-12-12 20:46:28 -08:00
parent a7e2fb31e3
commit 3c50508762
10 changed files with 421 additions and 201 deletions

View file

@ -364,16 +364,18 @@ format * smt2_pp_environment::pp_arith_literal(app * t, bool decimal, unsigned d
}
format * smt2_pp_environment::pp_string_literal(app * t) {
std::string s;
zstring s;
std::string encs;
VERIFY (get_sutil().str.is_string(t, s));
encs = s.encode();
std::ostringstream buffer;
buffer << "\"";
for (unsigned i = 0; i < s.length(); ++i) {
if (s[i] == '\"') {
for (unsigned i = 0; i < encs.length(); ++i) {
if (encs[i] == '\"') {
buffer << "\"\"";
}
else {
buffer << s[i];
buffer << encs[i];
}
}
buffer << "\"";

View file

@ -121,7 +121,7 @@ br_status seq_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * con
(a + string) + string = a + string
*/
br_status seq_rewriter::mk_seq_concat(expr* a, expr* b, expr_ref& result) {
std::string s1, s2;
zstring s1, s2;
expr* c, *d;
bool isc1 = m_util.str.is_string(a, s1);
bool isc2 = m_util.str.is_string(b, s2);
@ -150,10 +150,10 @@ br_status seq_rewriter::mk_seq_concat(expr* a, expr* b, expr_ref& result) {
}
br_status seq_rewriter::mk_seq_length(expr* a, expr_ref& result) {
std::string b;
zstring b;
m_es.reset();
m_util.str.get_concat(a, m_es);
size_t len = 0;
unsigned len = 0;
unsigned j = 0;
for (unsigned i = 0; i < m_es.size(); ++i) {
if (m_util.str.is_string(m_es[i], b)) {
@ -189,21 +189,21 @@ br_status seq_rewriter::mk_seq_length(expr* a, expr_ref& result) {
}
br_status seq_rewriter::mk_seq_extract(expr* a, expr* b, expr* c, expr_ref& result) {
std::string s;
zstring s;
rational pos, len;
if (m_util.str.is_string(a, s) && m_autil.is_numeral(b, pos) && m_autil.is_numeral(c, len) &&
pos.is_unsigned() && len.is_unsigned() && pos.get_unsigned() <= s.length()) {
unsigned _pos = pos.get_unsigned();
unsigned _len = len.get_unsigned();
result = m_util.str.mk_string(s.substr(_pos, _len));
result = m_util.str.mk_string(s.extract(_pos, _len));
return BR_DONE;
}
return BR_FAILED;
}
br_status seq_rewriter::mk_seq_contains(expr* a, expr* b, expr_ref& result) {
std::string c, d;
zstring c, d;
if (m_util.str.is_string(a, c) && m_util.str.is_string(b, d)) {
result = m().mk_bool_val(0 != strstr(c.c_str(), d.c_str()));
result = m().mk_bool_val(c.contains(d));
return BR_DONE;
}
// check if subsequence of b is in a.
@ -225,15 +225,12 @@ br_status seq_rewriter::mk_seq_contains(expr* a, expr* b, expr_ref& result) {
}
br_status seq_rewriter::mk_seq_at(expr* a, expr* b, expr_ref& result) {
std::string c;
zstring c;
rational r;
if (m_util.str.is_string(a, c) && m_autil.is_numeral(b, r) && r.is_unsigned()) {
unsigned j = r.get_unsigned();
if (j < c.length()) {
char ch = c[j];
c[0] = ch;
c[1] = 0;
result = m_util.str.mk_string(c);
result = m_util.str.mk_string(c.extract(j, 1));
return BR_DONE;
}
}
@ -241,19 +238,14 @@ br_status seq_rewriter::mk_seq_at(expr* a, expr* b, expr_ref& result) {
}
br_status seq_rewriter::mk_seq_index(expr* a, expr* b, expr* c, expr_ref& result) {
std::string s1, s2;
zstring s1, s2;
rational r;
bool isc1 = m_util.str.is_string(a, s1);
bool isc2 = m_util.str.is_string(b, s2);
if (isc1 && isc2 && m_autil.is_numeral(c, r) && r.is_unsigned()) {
for (unsigned i = r.get_unsigned(); i < s1.length(); ++i) {
if (strncmp(s1.c_str() + i, s2.c_str(), s2.length()) == 0) {
result = m_autil.mk_numeral(rational(i) - r, true);
return BR_DONE;
}
}
result = m_autil.mk_numeral(rational(-1), true);
int idx = s1.indexof(s2, r.get_unsigned());
result = m_autil.mk_numeral(rational(idx), true);
return BR_DONE;
}
if (m_autil.is_numeral(c, r) && r.is_neg()) {
@ -270,23 +262,10 @@ br_status seq_rewriter::mk_seq_index(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;
zstring 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 (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];
++i;
}
}
result = m_util.str.mk_string(buffer.str());
result = m_util.str.mk_string(s1.replace(s2, s3));
return BR_DONE;
}
if (b == c) {
@ -298,15 +277,11 @@ br_status seq_rewriter::mk_seq_replace(expr* a, expr* b, expr* c, expr_ref& resu
br_status seq_rewriter::mk_seq_prefix(expr* a, expr* b, expr_ref& result) {
TRACE("seq", tout << mk_pp(a, m()) << " " << mk_pp(b, m()) << "\n";);
std::string s1, s2;
zstring s1, s2;
bool isc1 = m_util.str.is_string(a, s1);
bool isc2 = m_util.str.is_string(b, s2);
if (isc1 && isc2) {
bool prefix = s1.length() <= s2.length();
for (unsigned i = 0; i < s1.length() && prefix; ++i) {
prefix = s1[i] == s2[i];
}
result = m().mk_bool_val(prefix);
result = m().mk_bool_val(s1.prefixof(s2));
return BR_DONE;
}
if (m_util.str.is_empty(a)) {
@ -321,7 +296,7 @@ br_status seq_rewriter::mk_seq_prefix(expr* a, expr* b, expr_ref& result) {
if (a1 != b1 && isc1 && isc2) {
if (s1.length() <= s2.length()) {
if (strncmp(s1.c_str(), s2.c_str(), s1.length()) == 0) {
if (s1.prefixof(s2)) {
if (a == a1) {
result = m().mk_true();
return BR_DONE;
@ -329,10 +304,10 @@ br_status seq_rewriter::mk_seq_prefix(expr* a, expr* b, expr_ref& result) {
m_util.str.get_concat(a, as);
m_util.str.get_concat(b, bs);
SASSERT(as.size() > 1);
s2 = std::string(s2.c_str() + s1.length(), s2.length() - s1.length());
s2 = s2.extract(s1.length(), s2.length()-s1.length());
bs[0] = m_util.str.mk_string(s2);
result = m_util.str.mk_prefix(m_util.str.mk_concat(as.size()-1, as.c_ptr()+1),
m_util.str.mk_concat(bs.size(), bs.c_ptr()));
m_util.str.mk_concat(bs.size(), bs.c_ptr()));
return BR_REWRITE_FULL;
}
else {
@ -341,7 +316,7 @@ br_status seq_rewriter::mk_seq_prefix(expr* a, expr* b, expr_ref& result) {
}
}
else {
if (strncmp(s1.c_str(), s2.c_str(), s2.length()) == 0) {
if (s2.prefixof(s1)) {
if (b == b1) {
result = m().mk_false();
return BR_DONE;
@ -349,7 +324,7 @@ br_status seq_rewriter::mk_seq_prefix(expr* a, expr* b, expr_ref& result) {
m_util.str.get_concat(a, as);
m_util.str.get_concat(b, bs);
SASSERT(bs.size() > 1);
s1 = std::string(s1.c_str() + s2.length(), s1.length() - s2.length());
s1 = s1.extract(s2.length(), s1.length() - s2.length());
as[0] = m_util.str.mk_string(s1);
result = m_util.str.mk_prefix(m_util.str.mk_concat(as.size(), as.c_ptr()),
m_util.str.mk_concat(bs.size()-1, bs.c_ptr()+1));
@ -396,7 +371,7 @@ br_status seq_rewriter::mk_seq_suffix(expr* a, expr* b, expr_ref& result) {
result = m().mk_true();
return BR_DONE;
}
std::string s1, s2;
zstring s1, s2;
if (m_util.str.is_empty(a)) {
result = m().mk_true();
return BR_DONE;
@ -438,21 +413,18 @@ br_status seq_rewriter::mk_seq_suffix(expr* a, expr* b, expr_ref& result) {
}
if (isc1 && isc2) {
if (s1.length() == s2.length()) {
SASSERT(s1 != s2);
//SASSERT(s1 != s2);
result = m().mk_false();
return BR_DONE;
}
else if (s1.length() < s2.length()) {
bool suffix = true;
for (unsigned i = 0; i < s1.length(); ++i) {
suffix = s1[s1.length()-i-1] == s2[s2.length()-i-1];
}
bool suffix = s1.suffixof(s2);
if (suffix && a1 == 0) {
result = m().mk_true();
return BR_DONE;
}
else if (suffix) {
s2 = std::string(s2.c_str(), s2.length()-s1.length());
s2 = s2.extract(0, s2.length()-s1.length());
b2 = m_util.str.mk_string(s2);
result = m_util.str.mk_suffix(a1, b1?m_util.str.mk_concat(b1, b2):b2);
return BR_DONE;
@ -468,12 +440,9 @@ br_status seq_rewriter::mk_seq_suffix(expr* a, expr* b, expr_ref& result) {
result = m().mk_false();
return BR_DONE;
}
bool suffix = true;
for (unsigned i = 0; i < s2.length(); ++i) {
suffix = s1[s1.length()-i-1] == s2[s2.length()-i-1];
}
bool suffix = s2.suffixof(s1);
if (suffix) {
s1 = std::string(s1.c_str(), s1.length()-s2.length());
s1 = s1.extract(0, s1.length()-s2.length());
a2 = m_util.str.mk_string(s1);
result = m_util.str.mk_suffix(a1?m_util.str.mk_concat(a1, a2):a2, b1);
return BR_DONE;
@ -491,14 +460,15 @@ br_status seq_rewriter::mk_seq_suffix(expr* a, expr* b, expr_ref& result) {
br_status seq_rewriter::mk_str_itos(expr* a, expr_ref& result) {
rational r;
if (m_autil.is_numeral(a, r)) {
result = m_util.str.mk_string(r.to_string());
result = m_util.str.mk_string(symbol(r.to_string().c_str()));
return BR_DONE;
}
return BR_FAILED;
}
br_status seq_rewriter::mk_str_stoi(expr* a, expr_ref& result) {
std::string s;
if (m_util.str.is_string(a, s)) {
zstring str;
if (m_util.str.is_string(a, str)) {
std::string s = str.encode();
for (unsigned i = 0; i < s.length(); ++i) {
if (s[i] == '-') { if (i != 0) return BR_FAILED; }
else if ('0' <= s[i] && s[i] <= '9') continue;
@ -608,13 +578,13 @@ bool seq_rewriter::reduce_eq(expr* l, expr* r, expr_ref_vector& lhs, expr_ref_ve
change = true;
}
// reduce strings
std::string s1, s2;
zstring s1, s2;
while (head1 < m_lhs.size() &&
head2 < m_rhs.size() &&
m_util.str.is_string(m_lhs[head1], s1) &&
m_util.str.is_string(m_rhs[head2], s2)) {
size_t l = std::min(s1.length(), s2.length());
for (size_t i = 0; i < l; ++i) {
unsigned l = std::min(s1.length(), s2.length());
for (unsigned i = 0; i < l; ++i) {
if (s1[i] != s2[i]) {
return false;
}
@ -623,14 +593,14 @@ bool seq_rewriter::reduce_eq(expr* l, expr* r, expr_ref_vector& lhs, expr_ref_ve
++head1;
}
else {
m_lhs[head1] = m_util.str.mk_string(std::string(s1.c_str()+l,s1.length()-l));
m_lhs[head1] = m_util.str.mk_string(s1.extract(l, s1.length()-l));
trail.push_back(m_lhs[head1]);
}
if (l == s2.length()) {
++head2;
}
else {
m_rhs[head2] = m_util.str.mk_string(std::string(s2.c_str()+l,s2.length()-l));
m_rhs[head2] = m_util.str.mk_string(s2.extract(l, s2.length()-l));
trail.push_back(m_rhs[head2]);
}
change = true;
@ -639,8 +609,8 @@ bool seq_rewriter::reduce_eq(expr* l, expr* r, expr_ref_vector& lhs, expr_ref_ve
head2 < m_rhs.size() &&
m_util.str.is_string(m_lhs.back(), s1) &&
m_util.str.is_string(m_rhs.back(), s2)) {
size_t l = std::min(s1.length(), s2.length());
for (size_t i = 0; i < l; ++i) {
unsigned l = std::min(s1.length(), s2.length());
for (unsigned i = 0; i < l; ++i) {
if (s1[s1.length()-i-1] != s2[s2.length()-i-1]) {
return false;
}
@ -648,11 +618,11 @@ bool seq_rewriter::reduce_eq(expr* l, expr* r, expr_ref_vector& lhs, expr_ref_ve
m_lhs.pop_back();
m_rhs.pop_back();
if (l < s1.length()) {
m_lhs.push_back(m_util.str.mk_string(std::string(s1.c_str(),s1.length()-l)));
m_lhs.push_back(m_util.str.mk_string(s1.extract(0, s1.length()-l)));
trail.push_back(m_lhs.back());
}
if (l < s2.length()) {
m_rhs.push_back(m_util.str.mk_string(std::string(s2.c_str(),s2.length()-l)));
m_rhs.push_back(m_util.str.mk_string(s2.extract(0, s2.length()-l)));
trail.push_back(m_rhs.back());
}
change = true;
@ -703,7 +673,7 @@ expr* seq_rewriter::concat_non_empty(unsigned n, expr* const* as) {
}
bool seq_rewriter::set_empty(unsigned sz, expr* const* es, bool all, expr_ref_vector& lhs, expr_ref_vector& rhs) {
std::string s;
zstring s;
for (unsigned i = 0; i < sz; ++i) {
if (m_util.str.is_unit(es[i])) {
if (all) return false;
@ -725,8 +695,8 @@ bool seq_rewriter::set_empty(unsigned sz, expr* const* es, bool all, expr_ref_ve
return true;
}
bool seq_rewriter::min_length(unsigned n, expr* const* es, size_t& len) {
std::string s;
bool seq_rewriter::min_length(unsigned n, expr* const* es, unsigned& len) {
zstring s;
bool bounded = true;
len = 0;
for (unsigned i = 0; i < n; ++i) {
@ -749,7 +719,7 @@ bool seq_rewriter::min_length(unsigned n, expr* const* es, size_t& len) {
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;
unsigned len1 = 0, len2 = 0;
bool bounded1 = min_length(szl, l, len1);
bool bounded2 = min_length(szr, r, len2);
if (bounded1 && len1 < len2) {

View file

@ -58,7 +58,7 @@ class seq_rewriter {
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);
bool min_length(unsigned n, expr* const* es, unsigned& len);
expr* concat_non_empty(unsigned n, expr* const* es);
public:

View file

@ -22,6 +22,145 @@ Revision History:
#include "ast_pp.h"
#include <sstream>
zstring::zstring(encoding enc): m_encoding(enc) {}
zstring::zstring(char const* s, encoding enc): m_encoding(enc) {
// TBD: epply decoding
while (*s) {
m_buffer.push_back(*s);
++s;
}
}
zstring::zstring(zstring const& other) {
m_buffer = other.m_buffer;
m_encoding = other.m_encoding;
}
zstring::zstring(unsigned num_bits, bool const* ch) {
SASSERT(num_bits == 8 || num_bits == 16);
m_encoding = (num_bits == 8)?ascii:unicode;
unsigned n = 0;
for (unsigned i = 0; i < num_bits; ++i) {
n |= (((unsigned)ch[i]) << num_bits);
}
m_buffer.push_back(n);
}
zstring::zstring(unsigned ch, encoding enc) {
m_encoding = enc;
m_buffer.push_back(ch & ((enc == ascii)?0x000000FF:0x0000FFFF));
}
zstring& zstring::operator=(zstring const& other) {
m_encoding = other.m_encoding;
m_buffer.reset();
m_buffer.append(other.m_buffer);
return *this;
}
zstring zstring::replace(zstring const& src, zstring const& dst) const {
zstring result(m_encoding);
if (length() < src.length()) {
return zstring(*this);
}
bool found = false;
for (unsigned i = 0; i <= length() - src.length(); ++i) {
bool eq = !found;
for (unsigned j = 0; eq && j < src.length(); ++j) {
eq = m_buffer[i+j] == src[j];
}
if (eq) {
result.m_buffer.append(dst.m_buffer);
found = true;
}
else {
result.m_buffer.push_back(m_buffer[i]);
}
}
return result;
}
std::string zstring::encode() const {
// TBD apply encodings.
SASSERT(m_encoding == ascii);
std::ostringstream strm;
for (unsigned i = 0; i < m_buffer.size(); ++i) {
strm << (char)(m_buffer[i]);
}
return strm.str();
}
bool zstring::suffixof(zstring const& other) const {
if (length() > other.length()) return false;
bool suffix = true;
for (unsigned i = 0; suffix && i < length(); ++i) {
suffix = m_buffer[length()-i-1] == other[other.length()-i-1];
}
return suffix;
}
bool zstring::prefixof(zstring const& other) const {
if (length() > other.length()) return false;
bool prefix = true;
for (unsigned i = 0; prefix && i < length(); ++i) {
prefix = m_buffer[i] == other[i];
}
return prefix;
}
bool zstring::contains(zstring const& other) const {
if (other.length() > length()) return false;
unsigned last = length() - other.length();
bool cont = false;
for (unsigned i = 0; !cont && i <= last; ++i) {
cont = true;
for (unsigned j = 0; cont && j < other.length(); ++j) {
cont = other[j] == m_buffer[j+i];
}
}
return cont;
}
int zstring::indexof(zstring const& other, int offset) const {
SASSERT(offset >= 0);
if (offset == length()) return -1;
if (other.length() + offset > length()) return -1;
unsigned last = length() - other.length();
for (unsigned i = static_cast<unsigned>(offset); i <= last; ++i) {
bool prefix = true;
for (unsigned j = 0; prefix && j < other.length(); ++j) {
prefix = m_buffer[i + j] == other[j];
}
if (prefix) {
return static_cast<int>(i);
}
}
return -1;
}
zstring zstring::extract(int offset, int len) const {
zstring result(m_encoding);
SASSERT(0 <= offset && 0 <= len);
int last = std::min(offset+len, static_cast<int>(length()));
for (int i = offset; i < last; ++i) {
result.m_buffer.push_back(m_buffer[i]);
}
return result;
}
zstring zstring::operator+(zstring const& other) const {
SASSERT(m_encoding == other.m_encoding);
zstring result(*this);
result.m_buffer.append(other.m_buffer);
return result;
}
std::ostream& zstring::operator<<(std::ostream& out) const {
return out << encode();
}
seq_decl_plugin::seq_decl_plugin(): m_init(false),
m_stringc_sym("String"),
m_string(0),
@ -452,6 +591,14 @@ app* seq_decl_plugin::mk_string(symbol const& s) {
return m_manager->mk_const(f);
}
app* seq_decl_plugin::mk_string(zstring const& s) {
symbol sym(s.encode().c_str());
parameter param(sym);
func_decl* f = m_manager->mk_const_decl(m_stringc_sym, m_string,
func_decl_info(m_family_id, OP_STRING_CONST, 1, &param));
return m_manager->mk_const(f);
}
bool seq_decl_plugin::is_value(app* e) const {
return is_app_of(e, m_family_id, OP_STRING_CONST);
}
@ -463,6 +610,18 @@ app* seq_util::mk_skolem(symbol const& name, unsigned n, expr* const* args, sort
return m.mk_app(f, n, args);
}
app* seq_util::str::mk_string(zstring const& s) { return u.seq.mk_string(s); }
bool seq_util::str::is_string(expr const* n, zstring& s) const {
if (is_string(n)) {
s = zstring(to_app(n)->get_decl()->get_parameter(0).get_symbol().bare_str());
return true;
}
else {
return false;
}
}
void seq_util::str::get_concat(expr* e, ptr_vector<expr>& es) const {
expr* e1, *e2;

View file

@ -81,6 +81,36 @@ enum seq_op_kind {
};
class zstring {
public:
enum encoding {
ascii,
unicode
};
private:
buffer<unsigned> m_buffer;
encoding m_encoding;
public:
zstring(encoding enc = ascii);
zstring(char const* s, encoding enc = ascii);
zstring(zstring const& other);
zstring(unsigned num_bits, bool const* ch);
zstring(unsigned ch, encoding enc = ascii);
zstring& operator=(zstring const& other);
zstring replace(zstring const& src, zstring const& dst) const;
unsigned num_bits() const { return (m_encoding==ascii)?8:16; }
std::string encode() const;
unsigned length() const { return m_buffer.size(); }
unsigned operator[](unsigned i) const { return m_buffer[i]; }
bool empty() const { return m_buffer.empty(); }
bool suffixof(zstring const& other) const;
bool prefixof(zstring const& other) const;
bool contains(zstring const& other) const;
int indexof(zstring const& other, int offset) const;
zstring extract(int lo, int hi) const;
zstring operator+(zstring const& other) const;
std::ostream& operator<<(std::ostream& out) const;
};
class seq_decl_plugin : public decl_plugin {
struct psig {
@ -146,6 +176,7 @@ public:
bool is_char(ast* a) const { return a == m_char; }
app* mk_string(symbol const& s);
app* mk_string(zstring const& s);
};
class seq_util {
@ -171,14 +202,18 @@ public:
seq_util& u;
ast_manager& m;
family_id m_fid;
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())); }
public:
str(seq_util& u): u(u), m(u.m), m_fid(u.m_fid) {}
sort* mk_seq(sort* s) { parameter param(s); return m.mk_sort(m_fid, SEQ_SORT, 1, &param); }
app* mk_empty(sort* s) { return m.mk_const(m.mk_func_decl(m_fid, OP_SEQ_EMPTY, 0, 0, 0, (expr*const*)0, s)); }
app* mk_string(zstring const& s);
app* mk_string(symbol const& s) { return u.seq.mk_string(s); }
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);
@ -193,14 +228,13 @@ public:
bool is_string(expr const * n) const { return is_app_of(n, m_fid, OP_STRING_CONST); }
bool is_string(expr const* n, std::string& s) const {
return is_string(n) && (s = to_app(n)->get_decl()->get_parameter(0).get_symbol().str(), true);
}
bool is_string(expr const* n, symbol& s) const {
return is_string(n) && (s = to_app(n)->get_decl()->get_parameter(0).get_symbol(), true);
}
bool is_string(expr const* n, zstring& s) const;
bool is_empty(expr const* n) const { symbol s;
return is_app_of(n, m_fid, OP_SEQ_EMPTY) || (is_string(n, s) && !s.is_numerical() && *s.bare_str() == 0);
}

View file

@ -1069,7 +1069,7 @@ namespace smt2 {
void parse_string_const() {
SASSERT(curr() == scanner::STRING_TOKEN);
expr_stack().push_back(sutil().str.mk_string(m_scanner.get_string()));
expr_stack().push_back(sutil().str.mk_string(symbol(m_scanner.get_string())));
TRACE("smt2parser", tout << "new string: " << mk_pp(expr_stack().back(), m()) << "\n";);
next();
}

View file

@ -815,7 +815,7 @@ namespace smt {
}
void setup::setup_seq() {
m_context.register_plugin(alloc(theory_seq_empty, m_manager));
m_context.register_plugin(alloc(theory_seq, m_manager));
}
void setup::setup_card() {

View file

@ -127,6 +127,7 @@ theory_seq::theory_seq(ast_manager& m):
m(m),
m_dam(m_dep_array_value_manager, m_alloc),
m_rep(m, m_dm),
m_cache(m),
m_sort2len_fn(m),
m_factory(0),
m_ineqs(m),
@ -135,6 +136,7 @@ theory_seq::theory_seq(ast_manager& m):
m_axioms_head(0),
m_branch_variable_head(0),
m_incomplete(false),
m_has_length(false),
m_model_completion(false),
m_rewrite(m),
m_util(m),
@ -249,10 +251,10 @@ bool theory_seq::find_branch_candidate(expr* l, ptr_vector<expr> const& rs) {
if (occurs(l, rs[j])) {
return false;
}
std::string s;
zstring 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));
v = m_util.str.mk_string(s.extract(0, k));
if (v0) v = m_util.str.mk_concat(v0, v);
if (assume_equality(l, v)) {
return true;
@ -426,6 +428,7 @@ bool theory_seq::is_right_select(expr* a, expr*& b) {
void theory_seq::add_solution(expr* l, expr* r, enode_pair_dependency* deps) {
context& ctx = get_context();
m_rep.update(l, r, deps);
m_cache.reset();
// TBD: skip new equalities for non-internalized terms.
if (ctx.e_internalized(l) && ctx.e_internalized(r)) {
propagate_eq(deps, ctx.get_enode(l), ctx.get_enode(r));
@ -508,6 +511,10 @@ bool theory_seq::internalize_term(app* term) {
}
mk_var(e);
}
if (m_util.str.is_length(term) && !m_has_length) {
m_trail_stack.push(value_trail<theory_seq, bool>(m_has_length));
m_has_length = true;
}
if (!m_util.str.is_concat(term) &&
!m_util.str.is_string(term) &&
!m_util.str.is_empty(term) &&
@ -635,37 +642,49 @@ expr_ref theory_seq::canonize(expr* e, enode_pair_dependency*& eqs) {
expr_ref theory_seq::expand(expr* e, enode_pair_dependency*& eqs) {
enode_pair_dependency* deps = 0;
expr* r = 0;
if (m_cache.find(e, r)) {
return expr_ref(r, m);
}
e = m_rep.find(e, deps);
expr_ref result(m);
expr* e1, *e2;
eqs = m_dm.mk_join(eqs, deps);
if (m_util.str.is_concat(e, e1, e2)) {
return expr_ref(m_util.str.mk_concat(expand(e1, eqs), expand(e2, eqs)), m);
result = m_util.str.mk_concat(expand(e1, eqs), expand(e2, eqs));
}
if (m_util.str.is_empty(e) || m_util.str.is_string(e)) {
return expr_ref(e, m);
else if (m_util.str.is_empty(e) || m_util.str.is_string(e)) {
result = e;
}
if (m.is_eq(e, e1, e2)) {
return expr_ref(m.mk_eq(expand(e1, eqs), expand(e2, eqs)), m);
else if (m.is_eq(e, e1, e2)) {
result = m.mk_eq(expand(e1, eqs), expand(e2, eqs));
}
if (m_util.str.is_prefix(e, e1, e2)) {
return expr_ref(m_util.str.mk_prefix(expand(e1, eqs), expand(e2, eqs)), m);
else if (m_util.str.is_prefix(e, e1, e2)) {
result = m_util.str.mk_prefix(expand(e1, eqs), expand(e2, eqs));
}
if (m_util.str.is_suffix(e, e1, e2)) {
return expr_ref(m_util.str.mk_suffix(expand(e1, eqs), expand(e2, eqs)), m);
else if (m_util.str.is_suffix(e, e1, e2)) {
result = m_util.str.mk_suffix(expand(e1, eqs), expand(e2, 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);
else if (m_util.str.is_contains(e, e1, e2)) {
result = m_util.str.mk_contains(expand(e1, eqs), expand(e2, eqs));
}
if (m_model_completion && is_var(e)) {
else 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;
result = val;
}
else {
result = e;
}
}
return expr_ref(e, m);
else {
result = e;
}
m_cache.insert(e, result);
return result;
}
void theory_seq::add_dependency(enode_pair_dependency*& dep, enode* a, enode* b) {
@ -707,48 +726,18 @@ void theory_seq::deque_axiom(expr* 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;
else if (m_util.str.is_unit(n)) {
add_length_unit_axiom(n);
}
SASSERT(n1->get_root() != n2->get_root());
if (!m_util.is_seq(n1->get_owner())) {
return;
else if (m_util.str.is_empty(n)) {
add_length_empty_axiom(n);
}
func_decl* f_len = 0;
if (!m_sort2len_fn.find(m.get_sort(n1->get_owner()), f_len)) {
return;
else if (m_util.str.is_concat(n)) {
add_length_concat_axiom(n);
}
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);
else if (m_util.str.is_string(n)) {
add_length_string_axiom(n);
}
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);
}
@ -760,7 +749,7 @@ void theory_seq::new_eq_len_concat(enode* n1, enode* n2) {
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) {
void theory_seq::tightest_prefix(expr* s, expr* x, literal lit1, literal lit2) {
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);
@ -768,26 +757,32 @@ void theory_seq::tightest_prefix(expr* s, expr* x, literal lit) {
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))));
add_axiom(lit1, lit2, s_eq_emp, mk_eq(s, s1c, false));
add_axiom(lit1, lit2, s_eq_emp, mk_eq(lc, one, false));
add_axiom(lit1, lit2, s_eq_emp, ~mk_literal(m_util.str.mk_contains(s, m_util.str.mk_concat(x, s1))));
}
/*
// index of s in t starting at offset.
let i = Index(t, s, 0):
len(t) = 0 => i = -1
len(t) != 0 & !contains(t, s) => i = -1
len(t) != 0 & contains(t, s) => t = xsy & i = len(x)
len(t) != 0 & contains(t, s) & s != emp => tightest_prefix(x, s)
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:
?
0 <= offset < len(t) => xy = t & len(x) = offset & (-1 = indexof(t, s, 0) => -1 = i)
& (indexof(t, s, 0) >= 0 => indexof(t, s, 0) + offset = i)
offset = len(t) => i = -1
if offset < 0 or offset >= len(t)
under specified
optional lemmas:
(len(s) > len(t) -> i = -1)
@ -801,20 +796,45 @@ void theory_seq::add_indexof_axiom(expr* i) {
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()) {
literal offset_ne_zero = null_literal;
bool is_num = m_autil.is_numeral(offset, r);
if (is_num && r.is_zero()) {
offset_ne_zero = null_literal;
}
else {
offset_ne_zero = ~mk_eq(offset, zero, false);
}
if (!is_num || 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);
add_axiom(offset_ne_zero, cnt, mk_eq(i, minus_one, false));
add_axiom(offset_ne_zero, ~eq_empty, mk_eq(i, zero, false));
add_axiom(offset_ne_zero, ~cnt, eq_empty, mk_eq(t, xsy, false));
tightest_prefix(s, x, ~cnt, offset_ne_zero);
}
else {
// TBD
if (is_num && r.is_zero()) {
return;
}
// offset >= len(t) => indexof(s, t, offset) = -1
expr_ref len_t(m_util.str.mk_length(t), m);
literal offset_ge_len = mk_literal(m_autil.mk_ge(mk_sub(offset, len_t), zero));
add_axiom(offset_ge_len, mk_eq(i, minus_one, false));
// 0 <= offset & offset < len(t) => t = xy
// 0 <= offset & offset < len(t) => len(x) = offset
// 0 <= offset & offset < len(t) & ~contains(s, y) => indexof(t, s, offset) = -1
// 0 <= offset & offset < len(t) & contains(s, y) => index(t, s, offset) = indexof(y, s, 0) + len(t)
expr_ref x = mk_skolem(symbol("seq.indexof.left"), t, s, offset);
expr_ref y = mk_skolem(symbol("seq.indexof.right"), t, s, offset);
expr_ref indexof(m_util.str.mk_index(y, s, zero), m);
// TBD:
//literal offset_ge_0 = mk_literal(m_autil.mk_ge(offset, zero));
//add_axiom(~offset_ge_0, offset_ge_len, mk_eq(indexof, i, false));
//add_axiom(~offset_ge_0, offset_ge_len, mk_eq(m_util.str.mk_length(x), offset, false));
//add_axiom(~offset_ge_0, offset_ge_len, mk_eq(t, m_util.str.mk_concat(x, y), false));
}
/*
@ -839,39 +859,56 @@ void theory_seq::add_replace_axiom(expr* r) {
tightest_prefix(s, x, ~cnt);
}
void theory_seq::add_length_unit_axiom(expr* n) {
if (!m_has_length) return;
SASSERT(m_util.str.is_unit(n));
expr_ref one(m_autil.mk_int(1), m), len(m_util.str.mk_length(n), m);
add_axiom(mk_eq(len, one, false));
}
void theory_seq::add_length_empty_axiom(expr* n) {
if (!m_has_length) return;
SASSERT(m_util.str.is_empty(n));
expr_ref zero(m_autil.mk_int(0), m), len(m_util.str.mk_length(n), m);
add_axiom(mk_eq(len, zero, false));
}
void theory_seq::add_length_string_axiom(expr* n) {
if (!m_has_length) return;
zstring s;
VERIFY(m_util.str.is_string(n, s));
expr_ref len(m_util.str.mk_length(n), m);
expr_ref ls(m_autil.mk_numeral(rational(s.length(), rational::ui64()), true), m);
add_axiom(mk_eq(len, ls, false));
}
void theory_seq::add_length_concat_axiom(expr* n) {
if (!m_has_length) return;
expr* a, *b;
VERIFY(m_util.str.is_concat(n, a, b));
expr_ref len(m_util.str.mk_length(n), m);
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(len, a_p_b, false));
}
/*
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;
expr* x;
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));
if (!m_util.str.is_unit(x) &&
!m_util.str.is_empty(x) &&
!m_util.str.is_string(x) &&
!m_util.str.is_concat(x)) {
expr_ref zero(m_autil.mk_int(0), m);
expr_ref emp(m_util.str.mk_empty(m.get_sort(x)), m);
literal eq1(mk_eq(zero, n, false));
literal eq2(mk_eq(x, emp, false));
add_axiom(mk_literal(m_autil.mk_ge(n, zero)));
@ -966,9 +1003,10 @@ void theory_seq::add_axiom(literal l1, literal l2, literal l3, literal l4) {
}
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, e2?2:1, es, m.get_sort(e1)), m);
expr_ref theory_seq::mk_skolem(symbol const& name, expr* e1, expr* e2, expr* e3) {
expr* es[3] = { e1, e2, e3 };
unsigned len = e3?3:(e2?2:1);
return expr_ref(m_util.mk_skolem(name, len, es, m.get_sort(e1)), m);
}
void theory_seq::propagate_eq(bool_var v, expr* e1, expr* e2) {
@ -1040,8 +1078,7 @@ void theory_seq::new_eq_eh(theory_var v1, theory_var v2) {
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);
// add length-equal axiom?
}
}
@ -1087,6 +1124,7 @@ void theory_seq::pop_scope_eh(unsigned num_scopes) {
m_rhs.pop_back();
m_deps.pop_back();
}
m_cache.reset();
}
void theory_seq::restart_eh() {
@ -1108,7 +1146,11 @@ void theory_seq::relevant_eh(app* 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)) {
m_util.str.is_at(n) ||
m_util.str.is_concat(n) ||
m_util.str.is_empty(n) ||
m_util.str.is_unit(n) ||
m_util.str.is_string(n)) {
enque_axiom(n);
}
}

View file

@ -87,6 +87,16 @@ namespace smt {
void display(std::ostream& out) const;
};
class eval_cache {
obj_map<expr, expr*> m_map;
expr_ref_vector m_trail;
public:
eval_cache(ast_manager& m): m_trail(m) {}
bool find(expr* v, expr*& r) const { return m_map.find(v, r); }
void insert(expr* v, expr* r) { m_trail.push_back(v); m_trail.push_back(r); m_map.insert(v, r); }
void reset() { m_map.reset(); m_trail.reset(); }
};
struct stats {
stats() { reset(); }
void reset() { memset(this, 0, sizeof(stats)); }
@ -101,6 +111,7 @@ namespace smt {
solution_map m_rep; // unification representative.
vector<expr_array> m_lhs, m_rhs; // persistent sets of equalities.
vector<enode_pair_dependency_array> m_deps; // persistent sets of dependencies.
eval_cache m_cache;
ast2ast_trailmap<sort, func_decl> m_sort2len_fn; // length functions per sort.
seq_factory* m_factory; // value factory
@ -110,6 +121,7 @@ namespace smt {
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_has_length; // is length applied
bool m_model_completion; // during model construction, invent values in canonizer
th_rewriter m_rewrite;
seq_util m_util;
@ -185,15 +197,16 @@ namespace smt {
void add_replace_axiom(expr* e);
void add_extract_axiom(expr* e);
void add_length_axiom(expr* n);
void add_length_unit_axiom(expr* n);
void add_length_empty_axiom(expr* n);
void add_length_concat_axiom(expr* n);
void add_length_string_axiom(expr* n);
void add_at_axiom(expr* n);
literal mk_literal(expr* n);
void tightest_prefix(expr* s, expr* x, literal lit);
void tightest_prefix(expr* s, expr* x, literal lit, literal lit2 = null_literal);
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);
expr_ref mk_skolem(symbol const& s, expr* e1, expr* e2 = 0, expr* e3 = 0);
void set_incomplete(app* term);

View file

@ -71,8 +71,8 @@ namespace smt {
}
virtual bool get_some_values(sort* s, expr_ref& v1, expr_ref& v2) {
if (u.is_string(s)) {
v1 = u.str.mk_string("a");
v2 = u.str.mk_string("b");
v1 = u.str.mk_string(symbol("a"));
v2 = u.str.mk_string(symbol("b"));
return true;
}
NOT_IMPLEMENTED_YET();