diff --git a/src/ast/seq_decl_plugin.cpp b/src/ast/seq_decl_plugin.cpp index afa3a023f..008f2b76c 100644 --- a/src/ast/seq_decl_plugin.cpp +++ b/src/ast/seq_decl_plugin.cpp @@ -1465,3 +1465,147 @@ bool seq_util::re::is_epsilon(expr* r) const { app* seq_util::re::mk_epsilon(sort* seq_sort) { return mk_to_re(u.str.mk_empty(seq_sort)); } + +/* + Produces compact view of concrete concatenations such as (abcd). +*/ +void seq_util::re::pp::compact_helper_seq(std::ostream& out, expr* s) const { + SASSERT(re.u.is_seq(s)); + if (re.m.is_value(s)) { + SASSERT(s->get_kind() == ast_kind::AST_APP); + if (re.u.str.is_concat(s)) { + expr_ref_vector es(re.m); + re.u.str.get_concat(s, es); + for (unsigned i = 0; i < es.size(); i++) + if (re.u.str.is_unit(es.get(i))) + seq_unit(out, es.get(i)); + else + out << mk_pp(es.get(i), re.m); + } + else if (re.u.str.is_empty(s)) + out << "()"; + else + seq_unit(out, s); + } + else + out << mk_pp(s, re.m); +} + +/* + Produces output such as [a-z] for a range. +*/ +void seq_util::re::pp::compact_helper_range(std::ostream& out, expr* s1, expr* s2) const { + out << "["; + if (re.u.str.is_unit(s1)) + seq_unit(out, s1); + else + out << mk_pp(s1, re.m); + out << "-"; + if (re.u.str.is_unit(s2)) + seq_unit(out, s2); + else + out << mk_pp(s1, re.m); + out << "]"; +} + +/* + Checks if parenthesis can be omitted in some cases in a loop body or in complement. +*/ +bool seq_util::re::pp::can_skip_parenth(expr* r) const { + expr* s; + return ((re.is_to_re(r, s) && re.u.str.is_unit(s)) || re.is_range(r) || re.is_empty(r) || re.is_epsilon(r)); +} + +/* + Specialize output for a unit sequence converting to visible ASCII characters if possible. +*/ +void seq_util::re::pp::seq_unit(std::ostream& out, expr* s) const { + expr* e; + if (re.u.str.is_unit(s, e)) { + rational r; + unsigned sz; + if (re.u.bv().is_numeral(e, r, sz) && sz == 8 && r.is_unsigned()) { + unsigned n = r.get_unsigned(); + if (32 < n && n < 127) + out << (char)n; + else if (n < 16) + out << "\\x0" << std::hex << n; + else + out << "\\x" << std::hex << n; + } + else + out << mk_pp(s, re.m); + } + else + out << mk_pp(s, re.m); +} + +/* + Pretty prints the regex r into the out stream +*/ +std::ostream& seq_util::re::pp::display(std::ostream& out) const { + expr* r1 = nullptr, * r2 = nullptr, * s = nullptr, * s2 = nullptr; + unsigned lo = 0, hi = 0; + if (re.is_full_char(e)) + return out << "."; + else if (re.is_full_seq(e)) + return out << ".*"; + else if (re.is_to_re(e, s)) + { + compact_helper_seq(out, s); + return out; + } + else if (re.is_range(e, s, s2)) { + compact_helper_range(out, s, s2); + return out; + } + else if (re.is_epsilon(e)) + return out << "()"; + else if (re.is_empty(e)) + return out << "[]"; + else if (re.is_concat(e, r1, r2)) + return out << pp(re, r1) << pp(re, r2); + else if (re.is_union(e, r1, r2)) + return out << pp(re, r1) << "|" << pp(re, r2); + else if (re.is_intersection(e, r1, r2)) + return out << "(" << pp(re, r1) << ")&(" << pp(re, r2) << ")"; + else if (re.is_complement(e, r1)) + if (can_skip_parenth(r1)) + return out << "~" << pp(re, r1); + else + return out << "~(" << pp(re, r1) << ")"; + else if (re.is_plus(e, r1)) + if (can_skip_parenth(r1)) + return out << pp(re, r1) << "+"; + else + return out << "(" << pp(re, r1) << ")+"; + else if (re.is_star(e, r1)) + if (can_skip_parenth(r1)) + return out << pp(re, r1) << "*"; + else + return out << "(" << pp(re, r1) << ")*"; + else if (re.is_loop(e, r1, lo)) + if (can_skip_parenth(r1)) + return out << pp(re, r1) << "{" << lo << ",}"; + else + return out << "(" << pp(re, r1) << "){" << lo << ",}"; + else if (re.is_loop(e, r1, lo, hi)) + if (can_skip_parenth(r1)) + return out << pp(re, r1) << "{" << lo << "," << hi << "}"; + else + return out << "(" << pp(re, r1) << "){" << lo << "," << hi << "}"; + else if (re.is_diff(e, r1, r2)) + return out << "(" << pp(re, r1) << ")\\(" << pp(re, r2) << ")"; + else if (re.m.is_ite(e, s, r1, r2)) + return out << "if(" << mk_pp(s, re.m) << "," << pp(re, r1) << "," << pp(re, r2) << ")"; + else if (re.is_opt(e, r1)) + if (can_skip_parenth(r1)) + return out << pp(re, r1) << "?"; + else + return out << "(" << pp(re, r1) << ")?"; + else if (re.is_reverse(e, r1)) + return out << "reverse(" << pp(re, r1) << ")"; + else + // Else: derivative or is_of_pred + return out << mk_pp(e, re.m); +} diff --git a/src/ast/seq_decl_plugin.h b/src/ast/seq_decl_plugin.h index eb2feba7a..9e1639729 100644 --- a/src/ast/seq_decl_plugin.h +++ b/src/ast/seq_decl_plugin.h @@ -412,6 +412,7 @@ public: seq_util& u; ast_manager& m; family_id m_fid; + public: re(seq_util& u): u(u), m(u.m), m_fid(u.m_fid) {} @@ -481,6 +482,20 @@ public: unsigned max_length(expr* r) const; bool is_epsilon(expr* r) const; app* mk_epsilon(sort* seq_sort); + + class pp { + seq_util::re& re; + expr* e; + bool can_skip_parenth(expr* r) const; + void seq_unit(std::ostream& out, expr* s) const; + void compact_helper_seq(std::ostream& out, expr* s) const; + void compact_helper_range(std::ostream& out, expr* s1, expr* s2) const; + + public: + pp(seq_util::re& r, expr* e) : re(r), e(e) {} + std::ostream& display(std::ostream&) const; + }; + }; str str; re re; @@ -499,4 +514,8 @@ public: }; +inline std::ostream& operator<<(std::ostream& out, seq_util::re::pp const & p) { return p.display(out); } + + + diff --git a/src/smt/seq_regex.cpp b/src/smt/seq_regex.cpp index 06b5e7f89..d025c2eee 100644 --- a/src/smt/seq_regex.cpp +++ b/src/smt/seq_regex.cpp @@ -791,6 +791,10 @@ namespace smt { STRACE("seq_regex_brief", tout << "(MAX SIZE REACHED) ";); return false; } + STRACE("seq_regex", tout << "Updating state graph for regex " + << mk_pp(r, m) << ") ";); + if (!m_state_graph.is_seen(r_id)) + STRACE("state_graph", tout << std::endl << "state(" << r_id << ") = " << seq_util::re::pp(re(), r) << std::endl;); // Add state m_state_graph.add_state(r_id); STRACE("state_graph", tout << "regex(" << r_id << ") = " << mk_pp(r, m) << std::endl;); @@ -811,7 +815,10 @@ namespace smt { for (auto const& dr: derivatives) { unsigned dr_id = get_state_id(dr); STRACE("seq_regex_verbose", tout - << " traversing deriv: " << dr_id << " " << std::endl;); + << std::endl << " traversing deriv: " << dr_id << " ";); + if (!m_state_graph.is_seen(dr_id)) + STRACE("state_graph", tout << "state(" << dr_id << ") = " << seq_util::re::pp(re(), dr) << std::endl;); + // Add state m_state_graph.add_state(dr_id); STRACE("state_graph", tout << "regex(" << dr_id << ") = " << mk_pp(dr, m) << std::endl;); bool maybecycle = can_be_in_cycle(r, dr);