mirror of
https://github.com/Z3Prover/z3
synced 2026-07-23 15:32:32 +00:00
use move constructor, re-enable split_set in seq_regex
This commit is contained in:
parent
361e0fba75
commit
2b4a473334
4 changed files with 185 additions and 36 deletions
|
|
@ -29,6 +29,7 @@ struct split_set::imp {
|
||||||
split_oracle m_filter;
|
split_oracle m_filter;
|
||||||
sort *m_re_sort = nullptr;
|
sort *m_re_sort = nullptr;
|
||||||
sort *m_seq_sort = nullptr; // sequence sort the decls are built for
|
sort *m_seq_sort = nullptr; // sequence sort the decls are built for
|
||||||
|
bool m_failure = false;
|
||||||
|
|
||||||
imp(seq_rewriter &rw, expr *r, unsigned threshold, split_oracle const &filter) : m(rw.m()), rw(rw),
|
imp(seq_rewriter &rw, expr *r, unsigned threshold, split_oracle const &filter) : m(rw.m()), rw(rw),
|
||||||
seq(rw.u()), re(rw.u().re), r(r, m), m_threshold(threshold), m_filter(filter) {
|
seq(rw.u()), re(rw.u().re), r(r, m), m_threshold(threshold), m_filter(filter) {
|
||||||
|
|
@ -61,8 +62,8 @@ struct split_set::iterator::imp {
|
||||||
split_set a_s, b_s;
|
split_set a_s, b_s;
|
||||||
split_set::iterator a_it, a_end;
|
split_set::iterator a_it, a_end;
|
||||||
split_set::iterator b_it, b_end;
|
split_set::iterator b_it, b_end;
|
||||||
intersection(seq_rewriter& rw, split_set const& a_src, split_set const& b_src)
|
intersection(seq_rewriter& rw, split_set&& a_src, split_set&& b_src)
|
||||||
: a_s(a_src), b_s(b_src),
|
: a_s(std::move(a_src)), b_s(std::move(b_src)),
|
||||||
a_it(a_s.begin()), a_end(a_s.end()),
|
a_it(a_s.begin()), a_end(a_s.end()),
|
||||||
b_it(b_s.begin()), b_end(b_s.end()) {}
|
b_it(b_s.begin()), b_end(b_s.end()) {}
|
||||||
bool at_end() const {
|
bool at_end() const {
|
||||||
|
|
@ -107,21 +108,22 @@ struct split_set::iterator::imp {
|
||||||
scoped_ptr<consumer> m_intersection;
|
scoped_ptr<consumer> m_intersection;
|
||||||
|
|
||||||
|
|
||||||
complement(split_set const &a) : a_s(a), it(a_s.begin()), end(a_s.end())
|
complement(split_set&& a) : a_s(std::move(a)), it(a_s.begin()), end(a_s.end())
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
void init() {
|
void init() {
|
||||||
if (m_init)
|
if (m_init)
|
||||||
return;
|
return;
|
||||||
m_init = true;
|
m_init = true;
|
||||||
expr_ref full(parent().seq.re.mk_full_seq(parent().i.m_re_sort), parent().m);
|
|
||||||
m_intersection = nullptr;
|
|
||||||
auto &p = parent();
|
auto &p = parent();
|
||||||
|
expr_ref full(p.seq.re.mk_full_seq(p.i.m_re_sort), p.m);
|
||||||
|
m_intersection = nullptr;
|
||||||
|
|
||||||
while (it != end && !it.failed()) {
|
while (it != end && !it.failed()) {
|
||||||
auto [a, b] = *it;
|
auto [a, b] = *it;
|
||||||
split_set A(p.i.rw, nullptr, p.i.m_threshold, p.i.m_filter);
|
split_set A(p.i.rw, nullptr, p.i.m_threshold, p.i.m_filter);
|
||||||
split_set B(p.i.rw, nullptr, p.i.m_threshold, p.i.m_filter);
|
split_set B(p.i.rw, nullptr, p.i.m_threshold, p.i.m_filter);
|
||||||
auto inter = alloc(intersection, p.i.rw, A, B);
|
auto inter = alloc(intersection, p.i.rw, std::move(A), std::move(B));
|
||||||
if (m_intersection) {
|
if (m_intersection) {
|
||||||
m_intersection->set_parent(*inter->a_it.m_imp);
|
m_intersection->set_parent(*inter->a_it.m_imp);
|
||||||
inter->a_it.m_imp->m_consumer = m_intersection.detach();
|
inter->a_it.m_imp->m_consumer = m_intersection.detach();
|
||||||
|
|
@ -140,7 +142,7 @@ struct split_set::iterator::imp {
|
||||||
else
|
else
|
||||||
p.push_split(full, full);
|
p.push_split(full, full);
|
||||||
if (it.failed())
|
if (it.failed())
|
||||||
p.m_failure = true;
|
p.set_failure();
|
||||||
}
|
}
|
||||||
|
|
||||||
void consume() override {
|
void consume() override {
|
||||||
|
|
@ -155,8 +157,8 @@ struct split_set::iterator::imp {
|
||||||
split_set::iterator a_it;
|
split_set::iterator a_it;
|
||||||
split_set::iterator a_end;
|
split_set::iterator a_end;
|
||||||
expr_ref b;
|
expr_ref b;
|
||||||
concat_left(split_set const &a_src, expr *b)
|
concat_left(split_set&& a_src, expr *b)
|
||||||
: a_s(a_src), a_it(a_s.begin()), a_end(a_s.end()), b(b, a_s.m_imp->m) {}
|
: a_s(std::move(a_src)), a_it(a_s.begin()), a_end(a_s.end()), b(b, a_s.m_imp->m) {}
|
||||||
|
|
||||||
void consume() override {
|
void consume() override {
|
||||||
while (a_it != a_end && !parent().has_split()) {
|
while (a_it != a_end && !parent().has_split()) {
|
||||||
|
|
@ -165,7 +167,7 @@ struct split_set::iterator::imp {
|
||||||
++a_it;
|
++a_it;
|
||||||
}
|
}
|
||||||
if (a_it.failed())
|
if (a_it.failed())
|
||||||
parent().m_failure = true;
|
parent().set_failure();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -174,7 +176,7 @@ struct split_set::iterator::imp {
|
||||||
split_set b_s;
|
split_set b_s;
|
||||||
split_set::iterator b_it;
|
split_set::iterator b_it;
|
||||||
split_set::iterator b_end;
|
split_set::iterator b_end;
|
||||||
concat_right(expr* a, split_set const &b_src) : a(a, b_src.m_imp->m), b_s(b_src), b_it(b_s.begin()), b_end(b_s.end()) {}
|
concat_right(expr* a, split_set&& b_src) : a(a, b_src.m_imp->m), b_s(std::move(b_src)), b_it(b_s.begin()), b_end(b_s.end()) {}
|
||||||
|
|
||||||
void consume() override {
|
void consume() override {
|
||||||
while (b_it != b_end && !parent().has_split()) {
|
while (b_it != b_end && !parent().has_split()) {
|
||||||
|
|
@ -183,7 +185,7 @@ struct split_set::iterator::imp {
|
||||||
++b_it;
|
++b_it;
|
||||||
}
|
}
|
||||||
if (b_it.failed())
|
if (b_it.failed())
|
||||||
parent().m_failure = true;
|
parent().set_failure();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -220,7 +222,7 @@ struct split_set::iterator::imp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (a_it.failed() || b_it.failed())
|
if (a_it.failed() || b_it.failed())
|
||||||
parent().m_failure = true;
|
parent().set_failure();
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
@ -243,6 +245,11 @@ struct split_set::iterator::imp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_failure() {
|
||||||
|
m_failure = true;
|
||||||
|
s.m_imp->m_failure = true;
|
||||||
|
}
|
||||||
|
|
||||||
bool has_split() {
|
bool has_split() {
|
||||||
SASSERT(m_init);
|
SASSERT(m_init);
|
||||||
return m_qhead < m_splits.size();
|
return m_qhead < m_splits.size();
|
||||||
|
|
@ -323,7 +330,7 @@ struct split_set::iterator::imp {
|
||||||
m_splits.push_back({a, b});
|
m_splits.push_back({a, b});
|
||||||
if (m_splits.size() > i.m_threshold) {
|
if (m_splits.size() > i.m_threshold) {
|
||||||
TRACE(seq, tout << "size of split set exceeds threshold");
|
TRACE(seq, tout << "size of split set exceeds threshold");
|
||||||
m_failure = true;
|
set_failure();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -344,14 +351,14 @@ struct split_set::iterator::imp {
|
||||||
if (re.is_intersection(r, a, b)) {
|
if (re.is_intersection(r, a, b)) {
|
||||||
split_set a_s(i.rw, a, i.m_threshold, {});
|
split_set a_s(i.rw, a, i.m_threshold, {});
|
||||||
split_set b_s(i.rw, b, i.m_threshold, {});
|
split_set b_s(i.rw, b, i.m_threshold, {});
|
||||||
m_consumer = alloc(intersection, i.rw, a_s, b_s);
|
m_consumer = alloc(intersection, i.rw, std::move(a_s), std::move(b_s));
|
||||||
m_consumer->set_parent(*this);
|
m_consumer->set_parent(*this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (re.is_complement(r, a)) {
|
if (re.is_complement(r, a)) {
|
||||||
split_set sigma_a(i.rw, a, i.m_threshold, {});
|
split_set sigma_a(i.rw, a, i.m_threshold, {});
|
||||||
m_consumer = alloc(complement, sigma_a);
|
m_consumer = alloc(complement, std::move(sigma_a));
|
||||||
m_consumer->set_parent(*this);
|
m_consumer->set_parent(*this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -392,9 +399,9 @@ struct split_set::iterator::imp {
|
||||||
// star: sigma(a*) = { <eps, eps> } cup a*.sigma(a).a*
|
// star: sigma(a*) = { <eps, eps> } cup a*.sigma(a).a*
|
||||||
auto add_star = [&](expr *r, expr* a) {
|
auto add_star = [&](expr *r, expr* a) {
|
||||||
split_set sigma_a(i.rw, a, i.m_threshold, {});
|
split_set sigma_a(i.rw, a, i.m_threshold, {});
|
||||||
auto *c_left = alloc(concat_left, sigma_a, r);
|
auto *c_left = alloc(concat_left, std::move(sigma_a), r);
|
||||||
split_set sigma_aa(i.rw, nullptr, i.m_threshold, {});
|
split_set sigma_aa(i.rw, nullptr, i.m_threshold, {});
|
||||||
auto *c_right = alloc(concat_right, r, sigma_aa);
|
auto *c_right = alloc(concat_right, r, std::move(sigma_aa));
|
||||||
auto &parent = *c_right->b_it.m_imp;
|
auto &parent = *c_right->b_it.m_imp;
|
||||||
parent.m_consumer = c_left;
|
parent.m_consumer = c_left;
|
||||||
c_left->set_parent(parent);
|
c_left->set_parent(parent);
|
||||||
|
|
@ -440,7 +447,7 @@ struct split_set::iterator::imp {
|
||||||
|
|
||||||
void set_failure(expr* r) {
|
void set_failure(expr* r) {
|
||||||
TRACE(seq, tout << "split_set::iterator::unfold: unhandled regex: " << mk_pp(r, m) << "\n");
|
TRACE(seq, tout << "split_set::iterator::unfold: unhandled regex: " << mk_pp(r, m) << "\n");
|
||||||
m_failure = true;
|
set_failure();
|
||||||
m_at_end = true;
|
m_at_end = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -457,8 +464,8 @@ split_set::~split_set() {
|
||||||
dealloc(m_imp);
|
dealloc(m_imp);
|
||||||
}
|
}
|
||||||
|
|
||||||
split_set::split_set(split_set const& other) {
|
split_set::split_set(split_set&& other) noexcept : m_imp(other.m_imp) {
|
||||||
m_imp = alloc(imp, other.m_imp->rw, other.m_imp->r, other.m_imp->m_threshold, other.m_imp->m_filter);
|
other.m_imp = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
split_set::iterator::iterator(split_set const &s, bool at_end) {
|
split_set::iterator::iterator(split_set const &s, bool at_end) {
|
||||||
|
|
@ -496,3 +503,73 @@ bool split_set::iterator::operator==(split_set::iterator const &other) const {
|
||||||
bool split_set::iterator::failed() const {
|
bool split_set::iterator::failed() const {
|
||||||
return m_imp->m_failure;
|
return m_imp->m_failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool split_set::failed() const {
|
||||||
|
return m_imp->m_failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<expr_ref, expr_ref> split_set::try_split_sequence(expr *str) {
|
||||||
|
ast_manager &m = m_imp->m;
|
||||||
|
auto &seq = m_imp->seq;
|
||||||
|
|
||||||
|
expr_ref_vector tokens(m);
|
||||||
|
vector<expr *> stack;
|
||||||
|
stack.push_back(str);
|
||||||
|
|
||||||
|
while (!stack.empty()) {
|
||||||
|
expr *cur = stack.back();
|
||||||
|
stack.pop_back();
|
||||||
|
expr *l, *r;
|
||||||
|
if (seq.str.is_concat(cur, l, r)) {
|
||||||
|
stack.push_back(r);
|
||||||
|
stack.push_back(l);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
tokens.push_back(expr_ref(cur, m));
|
||||||
|
}
|
||||||
|
|
||||||
|
expr *ch;
|
||||||
|
unsigned i = 0;
|
||||||
|
|
||||||
|
// TODO: Do this for the back as well (also, why did no rule before do that?)
|
||||||
|
|
||||||
|
if (tokens.empty())
|
||||||
|
return {expr_ref(m), expr_ref(m)};
|
||||||
|
|
||||||
|
// Choose the factorization boundary so the tail starts with the
|
||||||
|
// longest run of concrete characters c.
|
||||||
|
// This gives the split-engine lookahead oracle the most pruning information.
|
||||||
|
// head = u' (tokens before the run), tail = c <20> u''' (tokens from the run onward).
|
||||||
|
const unsigned total = tokens.size();
|
||||||
|
unsigned run_start = 0, run_len = 0;
|
||||||
|
for (i = 1; i < total;) {
|
||||||
|
if (!(seq.str.is_unit(tokens.get(i), ch) && seq.is_const_char(ch))) {
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
unsigned j = i;
|
||||||
|
while (j < total && seq.str.is_unit(tokens.get(j), ch) && seq.is_const_char(ch)) {
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
if (j - i > run_len) {
|
||||||
|
run_len = j - i;
|
||||||
|
run_start = i;
|
||||||
|
}
|
||||||
|
i = j;
|
||||||
|
}
|
||||||
|
// No constant run => fall back to splitting off the first token.
|
||||||
|
const unsigned p = run_len == 0 ? 1 : run_start;
|
||||||
|
SASSERT(p >= 1);
|
||||||
|
expr *head = tokens.get(0);
|
||||||
|
for (i = 1; i < p; i++) {
|
||||||
|
head = seq.str.mk_concat(head, tokens.get(i));
|
||||||
|
}
|
||||||
|
expr *tail = seq.str.mk_empty(head->get_sort());
|
||||||
|
if (tokens.size() > p + run_len) {
|
||||||
|
tail = tokens.get(p + run_len);
|
||||||
|
for (i = p + run_len + 1; i < tokens.size(); i++) {
|
||||||
|
tail = seq.str.mk_concat(tail, tokens.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {expr_ref(head, m), expr_ref(tail, m)};
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,9 @@ public:
|
||||||
|
|
||||||
~split_set();
|
~split_set();
|
||||||
|
|
||||||
split_set(split_set const& other);
|
split_set(split_set const& other) = delete;
|
||||||
|
|
||||||
|
split_set(split_set&& other) noexcept;
|
||||||
|
|
||||||
class iterator {
|
class iterator {
|
||||||
struct imp;
|
struct imp;
|
||||||
|
|
@ -67,4 +69,7 @@ public:
|
||||||
|
|
||||||
iterator begin() const;
|
iterator begin() const;
|
||||||
iterator end() const;
|
iterator end() const;
|
||||||
|
bool failed() const;
|
||||||
|
|
||||||
|
std::pair<expr_ref, expr_ref> try_split_sequence(expr *s);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,12 @@ namespace smt {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (unfold_prefix(lit)) {
|
||||||
|
TRACE(seq_regex, tout << "unfolded prefix" << std::endl;);
|
||||||
|
STRACE(seq_regex_brief, tout << "unfold_prefix ";);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (coallesce_in_re(lit)) {
|
if (coallesce_in_re(lit)) {
|
||||||
TRACE(seq_regex, tout
|
TRACE(seq_regex, tout
|
||||||
<< "simplified conjunctions to an intersection" << std::endl;);
|
<< "simplified conjunctions to an intersection" << std::endl;);
|
||||||
|
|
@ -121,6 +127,7 @@ namespace smt {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (is_string_equality(lit)) {
|
if (is_string_equality(lit)) {
|
||||||
TRACE(seq_regex, tout
|
TRACE(seq_regex, tout
|
||||||
<< "simplified regex using string equality" << std::endl;);
|
<< "simplified regex using string equality" << std::endl;);
|
||||||
|
|
@ -128,16 +135,43 @@ namespace smt {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
// TODO - replace this with a propagator closure that gets invoked and removed on backtracking.
|
||||||
// TODO - review
|
// it tracks <lit, split_set, in_re2 literals>
|
||||||
|
// an in_re2 literal is of the form in_re2(u, R1, v, R2)
|
||||||
|
// Assert in_re2(u, R1, v, R2) => u in R1 and v in R2
|
||||||
|
// forward on split_set until there is a new in_re2 literal that is not already false.
|
||||||
|
// If there was an already created in_re2 literal that is true,
|
||||||
|
// then check that the propagation axiom is true
|
||||||
|
// if it isn't true, then assert it.
|
||||||
|
// if it is true, we are done
|
||||||
|
// If split_set is done and all in_re2 literals are false, there is a conflict.
|
||||||
|
// Assert the conflict clause lit => (or in_re2 literals)
|
||||||
|
// Final check also unfolds this axiomatization
|
||||||
|
// (we have to add a final check to seq_regex for this).
|
||||||
|
|
||||||
if (th.get_fparams().m_seq_regex_factorization_enabled) {
|
if (th.get_fparams().m_seq_regex_factorization_enabled) {
|
||||||
unsigned threshold = th.get_fparams().m_seq_regex_factorization_threshold;
|
unsigned threshold = th.get_fparams().m_seq_regex_factorization_threshold;
|
||||||
if (threshold == 0)
|
expr_ref_vector prefix(m);
|
||||||
threshold = UINT_MAX;
|
expr *hd, *tl, *v;
|
||||||
split_set result;
|
auto filter = [&](expr* p, expr* _q) -> bool {
|
||||||
auto [head, tail] = seq_rw().split_membership(s, r, threshold, result);
|
expr_ref q(_q, m);
|
||||||
if (head) {
|
for (expr* v : prefix) {
|
||||||
SASSERT(tail);
|
q = seq_rw().mk_derivative(v, q);
|
||||||
|
if (re().is_empty(q))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return re().is_empty(q);
|
||||||
|
};
|
||||||
|
|
||||||
|
split_set result(seq_rw(), r, threshold, filter);
|
||||||
|
|
||||||
|
auto [head, tail] = result.try_split_sequence(s);
|
||||||
|
if (head && tail) {
|
||||||
|
tl = tail;
|
||||||
|
while (str().is_concat(tl, hd, tl) && str().is_unit(hd, v) && m.is_value(v)) {
|
||||||
|
prefix.push_back(v);
|
||||||
|
}
|
||||||
|
|
||||||
// propagate all cases
|
// propagate all cases
|
||||||
expr_ref_vector cases(m);
|
expr_ref_vector cases(m);
|
||||||
expr_ref_vector branches(m);
|
expr_ref_vector branches(m);
|
||||||
|
|
@ -146,14 +180,15 @@ namespace smt {
|
||||||
expr_ref mem_tail(re().mk_in_re(tail, post), m);
|
expr_ref mem_tail(re().mk_in_re(tail, post), m);
|
||||||
cases.push_back(m.mk_and(mem_head, mem_tail));
|
cases.push_back(m.mk_and(mem_head, mem_tail));
|
||||||
}
|
}
|
||||||
const expr_ref cases_expr(m.mk_or(cases), m);
|
if (!result.failed()) {
|
||||||
ctx.internalize(cases_expr, false);
|
const expr_ref cases_expr(m.mk_or(cases), m);
|
||||||
th.propagate_lit(nullptr, 1, &lit, ctx.get_literal(cases_expr));
|
ctx.internalize(cases_expr, false);
|
||||||
return;
|
th.propagate_lit(nullptr, 1, &lit, ctx.get_literal(cases_expr));
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// fallthrough; decomposition failed
|
// fallthrough; decomposition failed
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// Convert a non-ground sequence into an additional regex and
|
// Convert a non-ground sequence into an additional regex and
|
||||||
// strengthen the original regex constraint into an intersection
|
// strengthen the original regex constraint into an intersection
|
||||||
|
|
@ -384,6 +419,36 @@ namespace smt {
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool seq_regex::unfold_prefix(literal lit) {
|
||||||
|
expr *s = nullptr, *r = nullptr;
|
||||||
|
expr *e = ctx.bool_var2expr(lit.var());
|
||||||
|
VERIFY(str().is_in_re(e, s, r));
|
||||||
|
expr_ref_vector prefix(m);
|
||||||
|
expr *hd, *v, *tl = s;
|
||||||
|
while (str().is_concat(tl, hd, tl) && str().is_unit(hd, v) && m.is_value(v))
|
||||||
|
prefix.push_back(v);
|
||||||
|
|
||||||
|
if (prefix.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
expr_ref q(r, m);
|
||||||
|
for (expr *v : prefix) {
|
||||||
|
q = seq_rw().mk_derivative(v, q);
|
||||||
|
if (re().is_empty(q)) {
|
||||||
|
enode_pair_vector eqs;
|
||||||
|
literal_vector lits;
|
||||||
|
lits.push_back(~lit);
|
||||||
|
th.set_conflict(eqs, lits);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expr_ref fml(re().mk_in_re(tl, q), m);
|
||||||
|
rewrite(fml);
|
||||||
|
literal nlit = th.mk_literal(fml);
|
||||||
|
th.propagate_lit(nullptr, 1, &lit, nlit);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Combine a conjunction of membership relations for the same string
|
* Combine a conjunction of membership relations for the same string
|
||||||
* within the same Regex.
|
* within the same Regex.
|
||||||
|
|
|
||||||
|
|
@ -151,6 +151,8 @@ namespace smt {
|
||||||
|
|
||||||
bool block_unfolding(literal lit, unsigned i);
|
bool block_unfolding(literal lit, unsigned i);
|
||||||
|
|
||||||
|
bool unfold_prefix(literal lit);
|
||||||
|
|
||||||
expr_ref mk_first(expr* r, expr* n);
|
expr_ref mk_first(expr* r, expr* n);
|
||||||
|
|
||||||
bool is_member(expr* r, expr* u);
|
bool is_member(expr* r, expr* u);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue