mirror of
https://github.com/Z3Prover/z3
synced 2026-06-06 17:10:53 +00:00
tentative solution: use existing nullability check (we might want to check in the future which guards of the ITE are actually true)
This commit is contained in:
parent
f09f6d5097
commit
82df1afeaf
9 changed files with 33 additions and 85 deletions
|
|
@ -111,7 +111,6 @@ namespace euf {
|
||||||
case snode_kind::s_empty:
|
case snode_kind::s_empty:
|
||||||
n->m_ground = true;
|
n->m_ground = true;
|
||||||
n->m_regex_free = true;
|
n->m_regex_free = true;
|
||||||
n->m_nullable = true;
|
|
||||||
n->m_level = 0;
|
n->m_level = 0;
|
||||||
n->m_length = 0;
|
n->m_length = 0;
|
||||||
break;
|
break;
|
||||||
|
|
@ -119,7 +118,6 @@ namespace euf {
|
||||||
case snode_kind::s_char:
|
case snode_kind::s_char:
|
||||||
n->m_ground = true;
|
n->m_ground = true;
|
||||||
n->m_regex_free = true;
|
n->m_regex_free = true;
|
||||||
n->m_nullable = false;
|
|
||||||
n->m_level = 1;
|
n->m_level = 1;
|
||||||
n->m_length = 1;
|
n->m_length = 1;
|
||||||
break;
|
break;
|
||||||
|
|
@ -128,7 +126,6 @@ namespace euf {
|
||||||
// NSB review: a variable node can be a "value". Should it be ground then?
|
// NSB review: a variable node can be a "value". Should it be ground then?
|
||||||
n->m_ground = false;
|
n->m_ground = false;
|
||||||
n->m_regex_free = true;
|
n->m_regex_free = true;
|
||||||
n->m_nullable = false;
|
|
||||||
n->m_level = 1;
|
n->m_level = 1;
|
||||||
n->m_length = 1;
|
n->m_length = 1;
|
||||||
n->m_is_classical = false;
|
n->m_is_classical = false;
|
||||||
|
|
@ -138,7 +135,6 @@ namespace euf {
|
||||||
// NSB review: SASSERT(n->num_args() == 1); and simplify code
|
// NSB review: SASSERT(n->num_args() == 1); and simplify code
|
||||||
n->m_ground = n->num_args() > 0 ? n->arg(0)->is_ground() : true;
|
n->m_ground = n->num_args() > 0 ? n->arg(0)->is_ground() : true;
|
||||||
n->m_regex_free = true;
|
n->m_regex_free = true;
|
||||||
n->m_nullable = false;
|
|
||||||
n->m_level = 1;
|
n->m_level = 1;
|
||||||
n->m_length = 1;
|
n->m_length = 1;
|
||||||
break;
|
break;
|
||||||
|
|
@ -149,7 +145,6 @@ namespace euf {
|
||||||
snode* r = n->arg(1);
|
snode* r = n->arg(1);
|
||||||
n->m_ground = l->is_ground() && r->is_ground();
|
n->m_ground = l->is_ground() && r->is_ground();
|
||||||
n->m_regex_free = l->is_regex_free() && r->is_regex_free();
|
n->m_regex_free = l->is_regex_free() && r->is_regex_free();
|
||||||
n->m_nullable = l->is_nullable() && r->is_nullable();
|
|
||||||
n->m_is_classical = l->is_classical() && r->is_classical();
|
n->m_is_classical = l->is_classical() && r->is_classical();
|
||||||
n->m_level = std::max(l->level(), r->level()) + 1;
|
n->m_level = std::max(l->level(), r->level()) + 1;
|
||||||
n->m_length = l->length() + r->length();
|
n->m_length = l->length() + r->length();
|
||||||
|
|
@ -166,7 +161,6 @@ namespace euf {
|
||||||
snode* base = n->arg(0);
|
snode* base = n->arg(0);
|
||||||
n->m_ground = base->is_ground();
|
n->m_ground = base->is_ground();
|
||||||
n->m_regex_free = base->is_regex_free();
|
n->m_regex_free = base->is_regex_free();
|
||||||
n->m_nullable = base->is_nullable();
|
|
||||||
n->m_is_classical = base->is_classical();
|
n->m_is_classical = base->is_classical();
|
||||||
n->m_level = 1;
|
n->m_level = 1;
|
||||||
n->m_length = 1;
|
n->m_length = 1;
|
||||||
|
|
@ -178,7 +172,6 @@ namespace euf {
|
||||||
SASSERT(n->num_args() == 1);
|
SASSERT(n->num_args() == 1);
|
||||||
n->m_ground = n->arg(0)->is_ground();
|
n->m_ground = n->arg(0)->is_ground();
|
||||||
n->m_regex_free = false;
|
n->m_regex_free = false;
|
||||||
n->m_nullable = true;
|
|
||||||
n->m_is_classical = n->arg(0)->is_classical();
|
n->m_is_classical = n->arg(0)->is_classical();
|
||||||
n->m_level = 1;
|
n->m_level = 1;
|
||||||
n->m_length = 1;
|
n->m_length = 1;
|
||||||
|
|
@ -196,7 +189,6 @@ namespace euf {
|
||||||
if (n->get_expr() &&
|
if (n->get_expr() &&
|
||||||
!m_seq.re.is_loop(n->get_expr(), loop_body, lo, hi))
|
!m_seq.re.is_loop(n->get_expr(), loop_body, lo, hi))
|
||||||
m_seq.re.is_loop(n->get_expr(), loop_body, lo);
|
m_seq.re.is_loop(n->get_expr(), loop_body, lo);
|
||||||
n->m_nullable = (lo == 0);
|
|
||||||
n->m_level = 1;
|
n->m_level = 1;
|
||||||
n->m_length = 1;
|
n->m_length = 1;
|
||||||
break;
|
break;
|
||||||
|
|
@ -206,7 +198,6 @@ namespace euf {
|
||||||
SASSERT(n->num_args() == 2);
|
SASSERT(n->num_args() == 2);
|
||||||
n->m_ground = n->arg(0)->is_ground() && n->arg(1)->is_ground();
|
n->m_ground = n->arg(0)->is_ground() && n->arg(1)->is_ground();
|
||||||
n->m_regex_free = false;
|
n->m_regex_free = false;
|
||||||
n->m_nullable = n->arg(0)->is_nullable() || n->arg(1)->is_nullable();
|
|
||||||
n->m_is_classical = n->arg(0)->is_classical() && n->arg(1)->is_classical();
|
n->m_is_classical = n->arg(0)->is_classical() && n->arg(1)->is_classical();
|
||||||
n->m_level = 1;
|
n->m_level = 1;
|
||||||
n->m_length = 1;
|
n->m_length = 1;
|
||||||
|
|
@ -216,7 +207,6 @@ namespace euf {
|
||||||
SASSERT(n->num_args() == 2);
|
SASSERT(n->num_args() == 2);
|
||||||
n->m_ground = n->arg(0)->is_ground() && n->arg(1)->is_ground();
|
n->m_ground = n->arg(0)->is_ground() && n->arg(1)->is_ground();
|
||||||
n->m_regex_free = false;
|
n->m_regex_free = false;
|
||||||
n->m_nullable = n->arg(0)->is_nullable() && n->arg(1)->is_nullable();
|
|
||||||
n->m_is_classical = false;
|
n->m_is_classical = false;
|
||||||
n->m_level = 1;
|
n->m_level = 1;
|
||||||
n->m_length = 1;
|
n->m_length = 1;
|
||||||
|
|
@ -226,7 +216,6 @@ namespace euf {
|
||||||
SASSERT(n->num_args() == 1);
|
SASSERT(n->num_args() == 1);
|
||||||
n->m_ground = n->arg(0)->is_ground();
|
n->m_ground = n->arg(0)->is_ground();
|
||||||
n->m_regex_free = false;
|
n->m_regex_free = false;
|
||||||
n->m_nullable = !n->arg(0)->is_nullable();
|
|
||||||
n->m_is_classical = false;
|
n->m_is_classical = false;
|
||||||
n->m_level = 1;
|
n->m_level = 1;
|
||||||
n->m_length = 1;
|
n->m_length = 1;
|
||||||
|
|
@ -235,7 +224,6 @@ namespace euf {
|
||||||
case snode_kind::s_fail:
|
case snode_kind::s_fail:
|
||||||
n->m_ground = true;
|
n->m_ground = true;
|
||||||
n->m_regex_free = false;
|
n->m_regex_free = false;
|
||||||
n->m_nullable = false;
|
|
||||||
n->m_is_classical = false;
|
n->m_is_classical = false;
|
||||||
n->m_level = 1;
|
n->m_level = 1;
|
||||||
n->m_length = 1;
|
n->m_length = 1;
|
||||||
|
|
@ -244,7 +232,6 @@ namespace euf {
|
||||||
case snode_kind::s_full_char:
|
case snode_kind::s_full_char:
|
||||||
n->m_ground = true;
|
n->m_ground = true;
|
||||||
n->m_regex_free = false;
|
n->m_regex_free = false;
|
||||||
n->m_nullable = false;
|
|
||||||
n->m_level = 1;
|
n->m_level = 1;
|
||||||
n->m_length = 1;
|
n->m_length = 1;
|
||||||
break;
|
break;
|
||||||
|
|
@ -252,7 +239,6 @@ namespace euf {
|
||||||
case snode_kind::s_full_seq:
|
case snode_kind::s_full_seq:
|
||||||
n->m_ground = true;
|
n->m_ground = true;
|
||||||
n->m_regex_free = false;
|
n->m_regex_free = false;
|
||||||
n->m_nullable = true;
|
|
||||||
n->m_level = 1;
|
n->m_level = 1;
|
||||||
n->m_length = 1;
|
n->m_length = 1;
|
||||||
break;
|
break;
|
||||||
|
|
@ -261,7 +247,6 @@ namespace euf {
|
||||||
SASSERT(n->num_args() == 2);
|
SASSERT(n->num_args() == 2);
|
||||||
n->m_ground = n->arg(0)->is_ground() && n->arg(1)->is_ground();
|
n->m_ground = n->arg(0)->is_ground() && n->arg(1)->is_ground();
|
||||||
n->m_regex_free = false;
|
n->m_regex_free = false;
|
||||||
n->m_nullable = false;
|
|
||||||
n->m_level = 1;
|
n->m_level = 1;
|
||||||
n->m_length = 1;
|
n->m_length = 1;
|
||||||
break;
|
break;
|
||||||
|
|
@ -270,7 +255,6 @@ namespace euf {
|
||||||
SASSERT(n->num_args() == 1);
|
SASSERT(n->num_args() == 1);
|
||||||
n->m_ground = n->arg(0)->is_ground();
|
n->m_ground = n->arg(0)->is_ground();
|
||||||
n->m_regex_free = false;
|
n->m_regex_free = false;
|
||||||
n->m_nullable = n->arg(0)->is_nullable();
|
|
||||||
n->m_level = 1;
|
n->m_level = 1;
|
||||||
n->m_length = 1;
|
n->m_length = 1;
|
||||||
break;
|
break;
|
||||||
|
|
@ -279,7 +263,6 @@ namespace euf {
|
||||||
SASSERT(n->num_args() == 2);
|
SASSERT(n->num_args() == 2);
|
||||||
n->m_ground = n->arg(0)->is_ground() && n->arg(1)->is_ground();
|
n->m_ground = n->arg(0)->is_ground() && n->arg(1)->is_ground();
|
||||||
n->m_regex_free = false;
|
n->m_regex_free = false;
|
||||||
n->m_nullable = false;
|
|
||||||
n->m_level = 1;
|
n->m_level = 1;
|
||||||
n->m_length = 1;
|
n->m_length = 1;
|
||||||
break;
|
break;
|
||||||
|
|
@ -289,7 +272,6 @@ namespace euf {
|
||||||
// Is this UNREACHABLE()?
|
// Is this UNREACHABLE()?
|
||||||
n->m_ground = true;
|
n->m_ground = true;
|
||||||
n->m_regex_free = true;
|
n->m_regex_free = true;
|
||||||
n->m_nullable = false;
|
|
||||||
n->m_level = 1;
|
n->m_level = 1;
|
||||||
n->m_length = 1;
|
n->m_length = 1;
|
||||||
break;
|
break;
|
||||||
|
|
@ -773,8 +755,7 @@ namespace euf {
|
||||||
<< " level=" << n->level()
|
<< " level=" << n->level()
|
||||||
<< " len=" << n->length()
|
<< " len=" << n->length()
|
||||||
<< " ground=" << n->is_ground()
|
<< " ground=" << n->is_ground()
|
||||||
<< " rfree=" << n->is_regex_free()
|
<< " rfree=" << n->is_regex_free();
|
||||||
<< " nullable=" << n->is_nullable();
|
|
||||||
if (n->num_args() > 0) {
|
if (n->num_args() > 0) {
|
||||||
out << " args=(";
|
out << " args=(";
|
||||||
for (unsigned i = 0; i < n->num_args(); ++i) {
|
for (unsigned i = 0; i < n->num_args(); ++i) {
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,6 @@ namespace euf {
|
||||||
// metadata flags, analogous to ZIPT's Str/StrToken properties
|
// metadata flags, analogous to ZIPT's Str/StrToken properties
|
||||||
bool m_ground = true; // no uninterpreted string variables
|
bool m_ground = true; // no uninterpreted string variables
|
||||||
bool m_regex_free = true; // no regex constructs
|
bool m_regex_free = true; // no regex constructs
|
||||||
bool m_nullable = false; // accepts the empty string
|
|
||||||
bool m_is_classical = true; // classical regular expression
|
bool m_is_classical = true; // classical regular expression
|
||||||
unsigned m_level = 0; // tree depth/level (0 for empty, 1 for singletons)
|
unsigned m_level = 0; // tree depth/level (0 for empty, 1 for singletons)
|
||||||
unsigned m_length = 0; // token count, number of leaf tokens in the tree
|
unsigned m_length = 0; // token count, number of leaf tokens in the tree
|
||||||
|
|
@ -121,9 +120,6 @@ namespace euf {
|
||||||
bool is_regex_free() const {
|
bool is_regex_free() const {
|
||||||
return m_regex_free;
|
return m_regex_free;
|
||||||
}
|
}
|
||||||
bool is_nullable() const {
|
|
||||||
return m_nullable;
|
|
||||||
}
|
|
||||||
bool is_classical() const {
|
bool is_classical() const {
|
||||||
return m_is_classical;
|
return m_is_classical;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1613,15 +1613,13 @@ bool seq_util::rex::has_valid_info(expr* r) const {
|
||||||
seq_util::rex::info seq_util::rex::get_cached_info(expr* e) const {
|
seq_util::rex::info seq_util::rex::get_cached_info(expr* e) const {
|
||||||
if (has_valid_info(e))
|
if (has_valid_info(e))
|
||||||
return m_infos[e->get_id()];
|
return m_infos[e->get_id()];
|
||||||
else
|
return invalid_info;
|
||||||
return invalid_info;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Get the information value associated with the regular expression e
|
Get the information value associated with the regular expression e
|
||||||
*/
|
*/
|
||||||
seq_util::rex::info seq_util::rex::get_info(expr* e) const
|
seq_util::rex::info seq_util::rex::get_info(expr* e) const {
|
||||||
{
|
|
||||||
SASSERT(u.is_re(e));
|
SASSERT(u.is_re(e));
|
||||||
auto result = get_cached_info(e);
|
auto result = get_cached_info(e);
|
||||||
if (result.is_valid())
|
if (result.is_valid())
|
||||||
|
|
|
||||||
|
|
@ -166,12 +166,18 @@ namespace seq {
|
||||||
return m_str && m_str->length() == 1 && m_str->is_var() && m_regex->is_ground();
|
return m_str && m_str->length() == 1 && m_str->is_var() && m_regex->is_ground();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool str_mem::is_trivial() const {
|
bool str_mem::is_trivial(nielsen_node const* n) const {
|
||||||
return m_str && m_regex && m_str->is_empty() && m_regex->is_nullable();
|
if (!(m_str && m_regex && m_str->is_empty()))
|
||||||
|
return false;
|
||||||
|
const auto& info = n->graph().seq().re.get_info(m_regex->get_expr());
|
||||||
|
return info.nullable == l_true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool str_mem::is_contradiction() const {
|
bool str_mem::is_contradiction(nielsen_node const* n) const {
|
||||||
return (m_str && m_regex && m_str->is_empty() && !m_regex->is_nullable());
|
if (!(m_str && m_regex && m_str->is_empty()))
|
||||||
|
return false;
|
||||||
|
const auto& info = n->graph().seq().re.get_info(m_regex->get_expr());
|
||||||
|
return info.nullable == l_false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool str_mem::contains_var(euf::snode* var) const {
|
bool str_mem::contains_var(euf::snode* var) const {
|
||||||
|
|
@ -825,7 +831,7 @@ namespace seq {
|
||||||
unsigned wj = 0;
|
unsigned wj = 0;
|
||||||
for (unsigned j = 0; j < m_str_mem.size(); ++j) {
|
for (unsigned j = 0; j < m_str_mem.size(); ++j) {
|
||||||
str_mem& mem = m_str_mem[j];
|
str_mem& mem = m_str_mem[j];
|
||||||
if (mem.is_trivial())
|
if (mem.is_trivial(this))
|
||||||
continue;
|
continue;
|
||||||
m_str_mem[wj++] = mem;
|
m_str_mem[wj++] = mem;
|
||||||
}
|
}
|
||||||
|
|
@ -1136,7 +1142,7 @@ namespace seq {
|
||||||
|
|
||||||
// check for regex memberships that are immediately infeasible
|
// check for regex memberships that are immediately infeasible
|
||||||
for (str_mem& mem : m_str_mem) {
|
for (str_mem& mem : m_str_mem) {
|
||||||
if (mem.is_contradiction()) {
|
if (mem.is_contradiction(this)) {
|
||||||
TRACE(seq, tout << "contradiction " << mem_pp(m, mem) << "\n");
|
TRACE(seq, tout << "contradiction " << mem_pp(m, mem) << "\n");
|
||||||
set_general_conflict();
|
set_general_conflict();
|
||||||
set_conflict(backtrack_reason::regex, mem.m_dep);
|
set_conflict(backtrack_reason::regex, mem.m_dep);
|
||||||
|
|
@ -1175,7 +1181,7 @@ namespace seq {
|
||||||
bool nielsen_node::is_satisfied() const {
|
bool nielsen_node::is_satisfied() const {
|
||||||
if (any_of(m_str_eq, [](auto const &eq) { return !eq.is_trivial(); }))
|
if (any_of(m_str_eq, [](auto const &eq) { return !eq.is_trivial(); }))
|
||||||
return false;
|
return false;
|
||||||
if (any_of(m_str_mem, [](auto const &m) { return !m.is_trivial() && !m.is_primitive();}))
|
if (any_of(m_str_mem, [this](auto const &m) { return !m.is_trivial(this) && !m.is_primitive();}))
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -2418,7 +2424,7 @@ namespace seq {
|
||||||
str_mem const*& mem_out,
|
str_mem const*& mem_out,
|
||||||
bool& fwd) const {
|
bool& fwd) const {
|
||||||
for (str_mem const& mem : node->str_mems()) {
|
for (str_mem const& mem : node->str_mems()) {
|
||||||
SASSERT(mem.m_str && mem.m_regex && !mem.is_trivial());
|
SASSERT(mem.m_str && mem.m_regex && !mem.is_trivial(node));
|
||||||
|
|
||||||
for (unsigned od = 0; od < 2; ++od) {
|
for (unsigned od = 0; od < 2; ++od) {
|
||||||
bool local_fwd = (od == 0);
|
bool local_fwd = (od == 0);
|
||||||
|
|
|
||||||
|
|
@ -410,9 +410,10 @@ namespace seq {
|
||||||
// check if the constraint has the form x in R with x a single variable
|
// check if the constraint has the form x in R with x a single variable
|
||||||
bool is_primitive() const;
|
bool is_primitive() const;
|
||||||
|
|
||||||
bool is_trivial() const;
|
// TODO: These two functions need to aware of the truth of the ite-guards of the symbolic derivations
|
||||||
|
bool is_trivial(nielsen_node const* n) const;
|
||||||
|
|
||||||
bool is_contradiction() const;
|
bool is_contradiction(nielsen_node const* n) const;
|
||||||
|
|
||||||
// check if the constraint contains a given variable
|
// check if the constraint contains a given variable
|
||||||
bool contains_var(euf::snode* var) const;
|
bool contains_var(euf::snode* var) const;
|
||||||
|
|
|
||||||
|
|
@ -157,40 +157,6 @@ namespace seq {
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
bool seq_regex::compute_self_stabilizing(euf::snode* regex) const {
|
bool seq_regex::compute_self_stabilizing(euf::snode* regex) const {
|
||||||
if (!regex)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// R* is always self-stabilizing: D(c, R*) = D(c,R) · R*,
|
|
||||||
// so R* appears as the tail of every derivative and acts as
|
|
||||||
// its own stabilizer.
|
|
||||||
if (regex->is_star())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Σ* (full_seq, i.e., re.all / .*) is self-stabilizing:
|
|
||||||
// D(c, Σ*) = Σ* for every character c.
|
|
||||||
if (regex->is_full_seq())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// ∅ (fail / empty language) is trivially self-stabilizing:
|
|
||||||
// it has no live derivatives, so the flag is vacuously true.
|
|
||||||
if (regex->is_fail())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Complement of full_seq is ∅ (complement of Σ*), which is
|
|
||||||
// also trivially self-stabilizing.
|
|
||||||
if (regex->is_complement() && regex->num_args() == 1 &&
|
|
||||||
regex->arg(0)->is_full_seq())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Loop with lo=0 and no upper bound behaves like R*
|
|
||||||
// (r{0,} ≡ r*), so it is self-stabilizing.
|
|
||||||
if (regex->is_loop() && regex->is_nullable()) {
|
|
||||||
// A nullable loop with a star-like body: heuristic check.
|
|
||||||
// Only mark as self-stabilizing if the body is a Kleene closure.
|
|
||||||
// Loop(R, 0, ∞) ~ R* — but we rely on the sgraph to normalize
|
|
||||||
// these, so only catch exact star nodes above.
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -367,7 +333,7 @@ namespace seq {
|
||||||
re->is_full_char() || re->is_full_seq())
|
re->is_full_char() || re->is_full_seq())
|
||||||
return false;
|
return false;
|
||||||
// loop with lo == 0 accepts ε
|
// loop with lo == 0 accepts ε
|
||||||
if (re->is_loop() && re->is_nullable())
|
if (re->is_loop() && is_nullable(re))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
expr* e = re->get_expr();
|
expr* e = re->get_expr();
|
||||||
|
|
@ -394,7 +360,7 @@ namespace seq {
|
||||||
return true;
|
return true;
|
||||||
// loop(empty, lo, _) with lo > 0 is empty
|
// loop(empty, lo, _) with lo > 0 is empty
|
||||||
if (re->is_loop() && re->num_args() >= 1 && is_empty_regex(re->arg(0)))
|
if (re->is_loop() && re->num_args() >= 1 && is_empty_regex(re->arg(0)))
|
||||||
return !re->is_nullable(); // empty if not nullable (i.e., lo > 0)
|
return !is_nullable(re); // empty if not nullable (i.e., lo > 0)
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -497,7 +463,7 @@ namespace seq {
|
||||||
return l_undef;
|
return l_undef;
|
||||||
if (re->is_fail())
|
if (re->is_fail())
|
||||||
return l_true;
|
return l_true;
|
||||||
if (re->is_nullable())
|
if (is_nullable(re))
|
||||||
return l_false;
|
return l_false;
|
||||||
// Structural quick checks for kinds that are never empty
|
// Structural quick checks for kinds that are never empty
|
||||||
if (re->is_star() || re->is_full_char() || re->is_full_seq() || re->is_to_re() || re->is_range())
|
if (re->is_star() || re->is_full_char() || re->is_full_seq() || re->is_to_re() || re->is_range())
|
||||||
|
|
@ -554,7 +520,7 @@ namespace seq {
|
||||||
// std::cout << "Deriving by " << snode_label_html(ch, sg().get_manager()) << std::endl;
|
// std::cout << "Deriving by " << snode_label_html(ch, sg().get_manager()) << std::endl;
|
||||||
euf::snode* deriv = m_sg.brzozowski_deriv(current, ch);
|
euf::snode* deriv = m_sg.brzozowski_deriv(current, ch);
|
||||||
SASSERT(deriv);
|
SASSERT(deriv);
|
||||||
if (deriv->is_nullable())
|
if (is_nullable(deriv))
|
||||||
return l_false; // found an accepting state
|
return l_false; // found an accepting state
|
||||||
if (deriv->is_fail())
|
if (deriv->is_fail())
|
||||||
continue; // dead-end, no need to explore further
|
continue; // dead-end, no need to explore further
|
||||||
|
|
@ -699,7 +665,7 @@ namespace seq {
|
||||||
|
|
||||||
// check final state
|
// check final state
|
||||||
if (mem.m_str && mem.m_str->is_empty()) {
|
if (mem.m_str && mem.m_str->is_empty()) {
|
||||||
if (mem.m_regex->is_nullable())
|
if (is_nullable(mem.m_regex))
|
||||||
return simplify_status::satisfied;
|
return simplify_status::satisfied;
|
||||||
return simplify_status::conflict;
|
return simplify_status::conflict;
|
||||||
}
|
}
|
||||||
|
|
@ -737,7 +703,7 @@ namespace seq {
|
||||||
return 1;
|
return 1;
|
||||||
// empty string checks
|
// empty string checks
|
||||||
if (mem.m_str->is_empty()) {
|
if (mem.m_str->is_empty()) {
|
||||||
if (mem.m_regex->is_nullable())
|
if (is_nullable(mem.m_regex))
|
||||||
return 1;
|
return 1;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -768,15 +734,15 @@ namespace seq {
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
// Membership processing
|
// Membership processing
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
bool seq_regex::process_str_mem(seq::str_mem const& mem,
|
bool seq_regex::process_str_mem(str_mem const& mem,
|
||||||
vector<seq::str_mem>& out_mems) {
|
vector<str_mem>& out_mems) {
|
||||||
SASSERT(mem.m_str && mem.m_regex);
|
SASSERT(mem.m_str && mem.m_regex);
|
||||||
// empty string: check nullable
|
// empty string: check nullable
|
||||||
if (mem.m_str->is_empty())
|
if (mem.m_str->is_empty())
|
||||||
return mem.m_regex->is_nullable();
|
return is_nullable(mem.m_regex);
|
||||||
|
|
||||||
// consume ground prefix: derive regex by each leading concrete char
|
// consume ground prefix: derive regex by each leading concrete char
|
||||||
seq::str_mem working = mem;
|
seq::str_mem working = mem;
|
||||||
|
|
|
||||||
|
|
@ -211,7 +211,7 @@ namespace seq {
|
||||||
|
|
||||||
// check if regex accepts the empty string
|
// check if regex accepts the empty string
|
||||||
bool is_nullable(euf::snode* re) const {
|
bool is_nullable(euf::snode* re) const {
|
||||||
return re && re->is_nullable();
|
return re && seq.re.get_info(re->get_expr()).nullable == l_true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if regex is ground (no string variables)
|
// check if regex is ground (no string variables)
|
||||||
|
|
|
||||||
|
|
@ -558,7 +558,7 @@ namespace smt {
|
||||||
SASSERT(sat_node);
|
SASSERT(sat_node);
|
||||||
for (auto const& mem : sat_node->str_mems()) {
|
for (auto const& mem : sat_node->str_mems()) {
|
||||||
SASSERT(mem.m_str && mem.m_regex);
|
SASSERT(mem.m_str && mem.m_regex);
|
||||||
if (mem.is_trivial())
|
if (mem.is_trivial(sat_node))
|
||||||
continue; // empty string in nullable regex: already satisfied, no variable to constrain
|
continue; // empty string in nullable regex: already satisfied, no variable to constrain
|
||||||
VERIFY(mem.is_primitive()); // everything else should have been eliminated already
|
VERIFY(mem.is_primitive()); // everything else should have been eliminated already
|
||||||
euf::snode* first = mem.m_str->first();
|
euf::snode* first = mem.m_str->first();
|
||||||
|
|
|
||||||
|
|
@ -423,7 +423,7 @@ namespace smt {
|
||||||
}
|
}
|
||||||
|
|
||||||
// empty string in non-nullable regex → conflict
|
// empty string in non-nullable regex → conflict
|
||||||
if (mem.m_str->is_empty() && !mem.m_regex->is_nullable()) {
|
if (mem.m_str->is_empty() && m_seq.re.get_info(mem.m_regex->get_expr()).nullable == l_false) {
|
||||||
enode_pair_vector eqs;
|
enode_pair_vector eqs;
|
||||||
literal_vector lits;
|
literal_vector lits;
|
||||||
lits.push_back(mem.lit);
|
lits.push_back(mem.lit);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue