mirror of
https://github.com/Z3Prover/z3
synced 2025-04-23 09:05:31 +00:00
merge
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
commit
733f44d141
151 changed files with 3249 additions and 1504 deletions
|
@ -1399,6 +1399,7 @@ inline bool has_labels(expr const * n) {
|
|||
class some_value_proc {
|
||||
public:
|
||||
virtual expr * operator()(sort * s) = 0;
|
||||
virtual ~some_value_proc() = default;
|
||||
};
|
||||
|
||||
// -----------------------------------
|
||||
|
|
|
@ -46,6 +46,7 @@ public:
|
|||
public:
|
||||
virtual bool operator()(func_decl* d) const { return false; }
|
||||
virtual bool operator()(sort* s) const { return false; }
|
||||
virtual ~is_declared() = default;
|
||||
};
|
||||
private:
|
||||
ast_manager& m_manager;
|
||||
|
|
|
@ -406,7 +406,7 @@ namespace datatype {
|
|||
VALIDATE_PARAM(arity == 1 && num_parameters == 1 && parameters[0].is_ast() && is_func_decl(parameters[0].get_ast()));
|
||||
VALIDATE_PARAM(u().is_datatype(domain[0]));
|
||||
VALIDATE_PARAM_PP(domain[0] == to_func_decl(parameters[0].get_ast())->get_range(), "invalid sort argument passed to recognizer");
|
||||
// blindly trust that parameter is a constructor
|
||||
VALIDATE_PARAM_PP(u().is_constructor(to_func_decl(parameters[0].get_ast())), "expecting constructor argument to recognizer");
|
||||
sort* range = m_manager->mk_bool_sort();
|
||||
func_decl_info info(m_family_id, OP_DT_IS, num_parameters, parameters);
|
||||
info.m_private_parameters = true;
|
||||
|
|
|
@ -681,6 +681,7 @@ namespace euf {
|
|||
void egraph::begin_explain() {
|
||||
SASSERT(m_todo.empty());
|
||||
m_uses_congruence = false;
|
||||
DEBUG_CODE(for (enode* n : m_nodes) SASSERT(!n->is_marked1()););
|
||||
}
|
||||
|
||||
void egraph::end_explain() {
|
||||
|
|
|
@ -102,7 +102,7 @@ expr_ref bv2fpa_converter::convert_bv2fp(sort * s, expr * sgn, expr * exp, expr
|
|||
rational exp_unbiased_q;
|
||||
exp_unbiased_q = exp_q - m_fpa_util.fm().m_powers2.m1(ebits - 1);
|
||||
|
||||
scoped_mpz sig_z(mpzm);
|
||||
scoped_mpz sig_z(mpzm);
|
||||
mpf_exp_t exp_z;
|
||||
mpzm.set(sig_z, sig_q.to_mpq().numerator());
|
||||
exp_z = mpzm.get_int64(exp_unbiased_q.to_mpq().numerator());
|
||||
|
@ -346,7 +346,7 @@ void bv2fpa_converter::convert_consts(model_core * mc, model_core * target_model
|
|||
app * a0 = to_app(val->get_arg(0));
|
||||
|
||||
expr_ref v0(m), v1(m), v2(m);
|
||||
#ifdef Z3DEBUG
|
||||
#ifdef Z3DEBUG_FPA2BV_NAMES
|
||||
app * a1 = to_app(val->get_arg(1));
|
||||
app * a2 = to_app(val->get_arg(2));
|
||||
v0 = mc->get_const_interp(a0->get_decl());
|
||||
|
@ -378,7 +378,7 @@ void bv2fpa_converter::convert_consts(model_core * mc, model_core * target_model
|
|||
|
||||
SASSERT(val->is_app_of(m_fpa_util.get_family_id(), OP_FPA_FP));
|
||||
|
||||
#ifdef Z3DEBUG
|
||||
#ifdef Z3DEBUG_FPA2BV_NAMES
|
||||
SASSERT(to_app(val->get_arg(0))->get_decl()->get_arity() == 0);
|
||||
SASSERT(to_app(val->get_arg(1))->get_decl()->get_arity() == 0);
|
||||
SASSERT(to_app(val->get_arg(2))->get_decl()->get_arity() == 0);
|
||||
|
@ -386,9 +386,10 @@ void bv2fpa_converter::convert_consts(model_core * mc, model_core * target_model
|
|||
seen.insert(to_app(val->get_arg(1))->get_decl());
|
||||
seen.insert(to_app(val->get_arg(2))->get_decl());
|
||||
#else
|
||||
SASSERT(a->get_arg(0)->get_kind() == OP_EXTRACT);
|
||||
SASSERT(to_app(a->get_arg(0))->get_arg(0)->get_kind() == OP_EXTRACT);
|
||||
SASSERT(is_app(val->get_arg(0)));
|
||||
SASSERT(m_bv_util.is_extract(val->get_arg(0)));
|
||||
seen.insert(to_app(to_app(val->get_arg(0))->get_arg(0))->get_decl());
|
||||
|
||||
#endif
|
||||
|
||||
if (!sgn && !sig && !exp)
|
||||
|
|
|
@ -192,7 +192,7 @@ void fpa2bv_converter::mk_const(func_decl * f, expr_ref & result) {
|
|||
|
||||
app_ref sgn(m), s(m), e(m);
|
||||
|
||||
#ifdef Z3DEBUG
|
||||
#ifdef Z3DEBUG_FPA2BV_NAMES
|
||||
std::string p("fpa2bv");
|
||||
std::string name = f->get_name().str();
|
||||
|
||||
|
@ -326,7 +326,7 @@ void fpa2bv_converter::mk_rm_const(func_decl * f, expr_ref & result) {
|
|||
|
||||
expr_ref bv3(m);
|
||||
bv3 = m.mk_fresh_const(
|
||||
#ifdef Z3DEBUG
|
||||
#ifdef Z3DEBUG_FPA2BV_NAMES
|
||||
"fpa2bv_rm"
|
||||
#else
|
||||
nullptr
|
||||
|
@ -465,7 +465,7 @@ void fpa2bv_converter::add_core(unsigned sbits, unsigned ebits,
|
|||
|
||||
SASSERT(is_well_sorted(m, big_d_sig));
|
||||
if (ebits > sbits)
|
||||
throw default_exception("there is no floating point support for division for representations with non-standard bit representations");
|
||||
throw default_exception("addition/subtract with ebits > sbits not supported");
|
||||
|
||||
|
||||
expr_ref shifted_big(m), shifted_d_sig(m), sticky_raw(m), sticky(m);
|
||||
|
@ -950,7 +950,7 @@ void fpa2bv_converter::mk_div(sort * s, expr_ref & rm, expr_ref & x, expr_ref &
|
|||
unsigned ebits = m_util.get_ebits(s);
|
||||
unsigned sbits = m_util.get_sbits(s);
|
||||
if (ebits > sbits)
|
||||
throw default_exception("there is no floating point support for division for representations with non-standard bit representations");
|
||||
throw default_exception("division with ebits > sbits not supported");
|
||||
SASSERT(ebits <= sbits);
|
||||
|
||||
expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m), b_sgn(m), b_sig(m), b_exp(m), b_lz(m);
|
||||
|
@ -2561,9 +2561,7 @@ void fpa2bv_converter::mk_to_fp_float(sort * to_srt, expr * rm, expr * x, expr_r
|
|||
res_sig = sig;
|
||||
|
||||
res_sig = m_bv_util.mk_zero_extend(1, res_sig); // extra zero in the front for the rounder.
|
||||
unsigned sig_sz = m_bv_util.get_bv_size(res_sig);
|
||||
(void) sig_sz;
|
||||
SASSERT(sig_sz == to_sbits + 4);
|
||||
SASSERT(m_bv_util.get_bv_size(res_sig) == to_sbits + 4);
|
||||
|
||||
expr_ref exponent_overflow(m), exponent_underflow(m);
|
||||
exponent_overflow = m.mk_false();
|
||||
|
@ -2577,7 +2575,7 @@ void fpa2bv_converter::mk_to_fp_float(sort * to_srt, expr * rm, expr * x, expr_r
|
|||
lz_ext = m_bv_util.mk_zero_extend(to_ebits - from_ebits + 2, lz);
|
||||
res_exp = m_bv_util.mk_bv_sub(res_exp, lz_ext);
|
||||
}
|
||||
else if (from_ebits > (to_ebits + 2)) {
|
||||
else if (from_ebits >= (to_ebits + 2)) {
|
||||
unsigned ebits_diff = from_ebits - (to_ebits + 2);
|
||||
|
||||
// subtract lz for subnormal numbers.
|
||||
|
@ -2617,9 +2615,6 @@ void fpa2bv_converter::mk_to_fp_float(sort * to_srt, expr * rm, expr * x, expr_r
|
|||
res_exp = m.mk_ite(ovf_cond, max_exp, res_exp);
|
||||
res_exp = m.mk_ite(udf_cond, min_exp, res_exp);
|
||||
}
|
||||
else { // from_ebits == (to_ebits + 2)
|
||||
res_exp = m_bv_util.mk_bv_sub(exp, lz);
|
||||
}
|
||||
|
||||
SASSERT(m_bv_util.get_bv_size(res_exp) == to_ebits + 2);
|
||||
SASSERT(is_well_sorted(m, res_exp));
|
||||
|
@ -3839,7 +3834,7 @@ void fpa2bv_converter::mk_rounding_mode(decl_kind k, expr_ref & result)
|
|||
}
|
||||
|
||||
void fpa2bv_converter::dbg_decouple(const char * prefix, expr_ref & e) {
|
||||
#ifdef Z3DEBUG
|
||||
#ifdef Z3DEBUG_FPA2BV_NAMES
|
||||
return;
|
||||
// CMW: This works only for quantifier-free formulas.
|
||||
if (m_util.is_fp(e)) {
|
||||
|
|
|
@ -61,7 +61,7 @@ protected:
|
|||
public:
|
||||
|
||||
fpa2bv_converter(ast_manager & m);
|
||||
~fpa2bv_converter();
|
||||
virtual ~fpa2bv_converter();
|
||||
|
||||
fpa_util & fu() { return m_util; }
|
||||
bv_util & bu() { return m_bv_util; }
|
||||
|
|
|
@ -207,7 +207,7 @@ sort * fpa_decl_plugin::mk_float_sort(unsigned ebits, unsigned sbits) {
|
|||
m_manager->raise_exception("minimum number of exponent bits is 2");
|
||||
if (ebits > 63)
|
||||
m_manager->raise_exception("maximum number of exponent bits is 63");
|
||||
|
||||
|
||||
parameter p1(ebits), p2(sbits);
|
||||
parameter ps[2] = { p1, p2 };
|
||||
sort_size sz;
|
||||
|
@ -929,16 +929,22 @@ bool fpa_decl_plugin::is_unique_value(app* e) const {
|
|||
case OP_FPA_RM_TOWARD_NEGATIVE:
|
||||
case OP_FPA_RM_TOWARD_ZERO:
|
||||
return true;
|
||||
case OP_FPA_PLUS_INF: /* No; +oo == fp(#b0 #b11 #b00) */
|
||||
case OP_FPA_MINUS_INF: /* No; -oo == fp #b1 #b11 #b00) */
|
||||
case OP_FPA_PLUS_ZERO: /* No; +zero == fp #b0 #b00 #b000) */
|
||||
case OP_FPA_MINUS_ZERO: /* No; -zero == fp #b1 #b00 #b000) */
|
||||
case OP_FPA_PLUS_INF: /* No; +oo == (fp #b0 #b11 #b00) */
|
||||
case OP_FPA_MINUS_INF: /* No; -oo == (fp #b1 #b11 #b00) */
|
||||
case OP_FPA_PLUS_ZERO: /* No; +zero == (fp #b0 #b00 #b000) */
|
||||
case OP_FPA_MINUS_ZERO: /* No; -zero == (fp #b1 #b00 #b000) */
|
||||
case OP_FPA_NAN: /* No; NaN == (fp #b0 #b111111 #b0000001) */
|
||||
case OP_FPA_NUM: /* see NaN */
|
||||
return false;
|
||||
case OP_FPA_FP:
|
||||
return false; /*No; generally not because of clashes with +oo, -oo, +zero, -zero, NaN */
|
||||
// a refinement would require to return true only if there is no clash with these cases.
|
||||
case OP_FPA_FP: {
|
||||
if (m_manager->is_value(e->get_arg(0)) &&
|
||||
m_manager->is_value(e->get_arg(1)) &&
|
||||
m_manager->is_value(e->get_arg(2))) {
|
||||
bv_util bu(*m_manager);
|
||||
return !bu.is_allone(e->get_arg(1)) && !bu.is_zero(e->get_arg(1));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ Revision History:
|
|||
|
||||
class is_variable_proc {
|
||||
public:
|
||||
virtual ~is_variable_proc() = default;
|
||||
virtual bool operator()(const expr* e) const = 0;
|
||||
};
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ Notes:
|
|||
|
||||
class expr_predicate {
|
||||
public:
|
||||
virtual ~expr_predicate() = default;
|
||||
virtual bool operator()(expr * t) = 0;
|
||||
};
|
||||
|
||||
|
|
|
@ -37,6 +37,8 @@ public:
|
|||
m_ignore_quantifiers(ignore_quantifiers) {
|
||||
}
|
||||
|
||||
virtual ~num_occurs() = default;
|
||||
|
||||
void validate();
|
||||
virtual void reset() { m_num_occurs.reset(); }
|
||||
|
||||
|
|
|
@ -93,6 +93,7 @@ namespace recfun {
|
|||
// closure for computing whether a `rhs` expression is immediate
|
||||
struct is_immediate_pred {
|
||||
virtual bool operator()(expr * rhs) = 0;
|
||||
virtual ~is_immediate_pred() = default;
|
||||
};
|
||||
|
||||
class def {
|
||||
|
|
|
@ -703,8 +703,60 @@ br_status array_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result)
|
|||
result = m().update_quantifier(lam, quantifier_kind::forall_k, e);
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
expr_ref lh1(m()), rh1(m());
|
||||
|
||||
expr_ref_vector fmls(m());
|
||||
|
||||
|
||||
auto has_large_domain = [&](sort* s, unsigned num_stores) {
|
||||
unsigned sz = get_array_arity(s);
|
||||
uint64_t dsz = 1;
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
sort* d = get_array_domain(s, i);
|
||||
if (d->is_infinite() || d->is_very_big())
|
||||
return true;
|
||||
auto const& n = d->get_num_elements();
|
||||
if (n.size() > num_stores)
|
||||
return true;
|
||||
dsz *= n.size();
|
||||
if (dsz > num_stores)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
if (m_expand_store_eq) {
|
||||
expr* lhs1 = lhs;
|
||||
expr* rhs1 = rhs;
|
||||
unsigned num_lhs = 0, num_rhs = 0;
|
||||
while (m_util.is_store(lhs1)) {
|
||||
lhs1 = to_app(lhs1)->get_arg(0);
|
||||
++num_lhs;
|
||||
}
|
||||
while (m_util.is_store(rhs1)) {
|
||||
rhs1 = to_app(rhs1)->get_arg(0);
|
||||
++num_rhs;
|
||||
}
|
||||
if (lhs1 == rhs1) {
|
||||
mk_eq(lhs, lhs, rhs, fmls);
|
||||
mk_eq(rhs, lhs, rhs, fmls);
|
||||
result = m().mk_and(fmls);
|
||||
return BR_REWRITE_FULL;
|
||||
}
|
||||
|
||||
if (m_util.is_const(lhs1, v) && m_util.is_const(rhs1, w) &&
|
||||
has_large_domain(lhs->get_sort(), std::max(num_lhs, num_rhs))) {
|
||||
mk_eq(lhs, lhs, rhs, fmls);
|
||||
mk_eq(rhs, lhs, rhs, fmls);
|
||||
fmls.push_back(m().mk_eq(v, w));
|
||||
result = m().mk_and(fmls);
|
||||
return BR_REWRITE_FULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (m_expand_nested_stores) {
|
||||
expr_ref lh1(m()), rh1(m());
|
||||
if (is_expandable_store(lhs)) {
|
||||
lh1 = expand_store(lhs);
|
||||
}
|
||||
|
@ -719,10 +771,6 @@ br_status array_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result)
|
|||
}
|
||||
}
|
||||
|
||||
if (!m_expand_store_eq) {
|
||||
return BR_FAILED;
|
||||
}
|
||||
expr_ref_vector fmls(m());
|
||||
|
||||
#if 0
|
||||
// lambda friendly version of array equality rewriting.
|
||||
|
@ -744,46 +792,5 @@ br_status array_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result)
|
|||
}
|
||||
#endif
|
||||
|
||||
expr* lhs1 = lhs;
|
||||
unsigned num_lhs = 0, num_rhs = 0;
|
||||
while (m_util.is_store(lhs1)) {
|
||||
lhs1 = to_app(lhs1)->get_arg(0);
|
||||
++num_lhs;
|
||||
}
|
||||
expr* rhs1 = rhs;
|
||||
while (m_util.is_store(rhs1)) {
|
||||
rhs1 = to_app(rhs1)->get_arg(0);
|
||||
++num_rhs;
|
||||
}
|
||||
if (lhs1 == rhs1) {
|
||||
mk_eq(lhs, lhs, rhs, fmls);
|
||||
mk_eq(rhs, lhs, rhs, fmls);
|
||||
result = m().mk_and(fmls);
|
||||
return BR_REWRITE_FULL;
|
||||
}
|
||||
auto has_large_domain = [&](sort* s, unsigned num_stores) {
|
||||
unsigned sz = get_array_arity(s);
|
||||
uint64_t dsz = 1;
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
sort* d = get_array_domain(s, i);
|
||||
if (d->is_infinite() || d->is_very_big())
|
||||
return true;
|
||||
auto const& n = d->get_num_elements();
|
||||
if (n.size() > num_stores)
|
||||
return true;
|
||||
dsz *= n.size();
|
||||
if (dsz > num_stores)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
if (m_util.is_const(lhs1, v) && m_util.is_const(rhs1, w) &&
|
||||
has_large_domain(lhs->get_sort(), std::max(num_lhs, num_rhs))) {
|
||||
mk_eq(lhs, lhs, rhs, fmls);
|
||||
mk_eq(rhs, lhs, rhs, fmls);
|
||||
fmls.push_back(m().mk_eq(v, w));
|
||||
result = m().mk_and(fmls);
|
||||
return BR_REWRITE_FULL;
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ struct push_app_ite_cfg : public default_rewriter_cfg {
|
|||
virtual bool is_target(func_decl * decl, unsigned num_args, expr * const * args);
|
||||
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr);
|
||||
push_app_ite_cfg(ast_manager& m): m(m), m_conservative(true) {}
|
||||
virtual ~push_app_ite_cfg() = default;
|
||||
void set_conservative(bool c) { m_conservative = c; }
|
||||
bool rewrite_patterns() const { return false; }
|
||||
};
|
||||
|
|
|
@ -22,10 +22,9 @@ Author:
|
|||
|
||||
br_status recfun_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
if (m_rec.is_defined(f) && num_args > 0) {
|
||||
for (unsigned i = 0; i < num_args; ++i) {
|
||||
for (unsigned i = 0; i < num_args; ++i)
|
||||
if (!m.is_value(args[i]))
|
||||
return BR_FAILED;
|
||||
}
|
||||
if (!m_rec.has_def(f))
|
||||
return BR_FAILED;
|
||||
recfun::def const& d = m_rec.get_def(f);
|
||||
|
@ -35,9 +34,8 @@ br_status recfun_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr *
|
|||
result = sub(d.get_rhs(), num_args, args);
|
||||
return BR_REWRITE_FULL;
|
||||
}
|
||||
else {
|
||||
else
|
||||
return BR_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -850,7 +850,7 @@ namespace seq {
|
|||
add_clause(~eq, ge10k);
|
||||
|
||||
for (unsigned i = 0; i < k; ++i) {
|
||||
expr* ch = seq.str.mk_nth_i(ubvs, i);
|
||||
expr* ch = seq.str.mk_nth_c(ubvs, i);
|
||||
is_digit = seq.mk_char_is_digit(ch);
|
||||
add_clause(~ge_len, is_digit);
|
||||
}
|
||||
|
@ -1142,8 +1142,8 @@ namespace seq {
|
|||
|
||||
/**
|
||||
~contains(a, b) => ~prefix(b, a)
|
||||
~contains(a, b) => ~contains(tail(a), b) or a = empty
|
||||
~contains(a, b) & a = empty => b != empty
|
||||
~contains(a, b) => ~contains(tail(a), b)
|
||||
a = empty => tail(a) = empty
|
||||
~(a = empty) => a = head + tail
|
||||
*/
|
||||
void axioms::unroll_not_contains(expr* e) {
|
||||
|
@ -1165,7 +1165,7 @@ namespace seq {
|
|||
expr_ref bound_tracker = m_sk.mk_length_limit(s, k);
|
||||
expr* s0 = nullptr;
|
||||
if (seq.str.is_stoi(s, s0))
|
||||
s = s0;
|
||||
s = s0;
|
||||
add_clause(~bound_tracker, mk_le(mk_len(s), k));
|
||||
return bound_tracker;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ Author:
|
|||
|
||||
--*/
|
||||
|
||||
#include "ast/ast_pp.h"
|
||||
#include "ast/rewriter/seq_eq_solver.h"
|
||||
#include "ast/bv_decl_plugin.h"
|
||||
|
||||
|
@ -675,7 +676,7 @@ namespace seq {
|
|||
if (rs.size() > i) {
|
||||
unsigned diff = rs.size() - (i + 1);
|
||||
for (unsigned j = 0; same && j < i; ++j)
|
||||
same = !m.are_distinct(ls[j], rs[diff + j]);
|
||||
same = !m.are_distinct(ls[j], rs[diff + j]);
|
||||
}
|
||||
// ls = x ++ rs ++ y, diff = |x|
|
||||
else {
|
||||
|
@ -704,8 +705,9 @@ namespace seq {
|
|||
bool same = true;
|
||||
// ls = x ++ rs' && rs = rs' ++ y, diff = |x|
|
||||
if (rs.size() > i) {
|
||||
for (unsigned j = 1; same && j <= i; ++j)
|
||||
same = !m.are_distinct(ls[diff + j], rs[j]);
|
||||
for (unsigned j = 1; same && j <= i; ++j) {
|
||||
same = !m.are_distinct(ls[diff + j], rs[j]);
|
||||
}
|
||||
}
|
||||
// ls = x ++ rs ++ y, diff = |x|
|
||||
else {
|
||||
|
@ -715,6 +717,7 @@ namespace seq {
|
|||
if (same)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@ namespace seq {
|
|||
|
||||
class eq_solver_context {
|
||||
public:
|
||||
virtual ~eq_solver_context() = default;
|
||||
virtual void add_consequence(bool uses_dep, expr_ref_vector const& clause) = 0;
|
||||
virtual void add_solution(expr* var, expr* term) = 0;
|
||||
virtual expr* expr2rep(expr* e) = 0;
|
||||
|
|
|
@ -859,13 +859,12 @@ br_status seq_rewriter::mk_seq_length(expr* a, expr_ref& result) {
|
|||
// elif offset >= len(s) then 0
|
||||
// elif offset + length > len(s) then len(s) - offset
|
||||
// else length
|
||||
expr_ref zero(m_autil.mk_int(0), m());
|
||||
result = length;
|
||||
result = m().mk_ite(m_autil.mk_gt(m_autil.mk_add(offset, length), len_s),
|
||||
m_autil.mk_sub(len_s, offset),
|
||||
result);
|
||||
result = m().mk_ite(m().mk_or(m_autil.mk_le(len_s, offset), m_autil.mk_le(length, zero), m_autil.mk_lt(offset, zero)),
|
||||
zero,
|
||||
result = m().mk_ite(m().mk_or(m_autil.mk_le(len_s, offset), m_autil.mk_le(length, zero()), m_autil.mk_lt(offset, zero())),
|
||||
zero(),
|
||||
result);
|
||||
return BR_REWRITE_FULL;
|
||||
}
|
||||
|
@ -883,52 +882,75 @@ expr_ref seq_rewriter::mk_seq_first(expr* t) {
|
|||
if (str().is_extract(t, s, j, k))
|
||||
result = str().mk_nth_i(s, j);
|
||||
else
|
||||
result = str().mk_nth_i(t, m_autil.mk_int(0));
|
||||
result = str().mk_nth_c(t, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
expr_ref seq_rewriter::mk_sub(expr* a, rational const& n) {
|
||||
expr* a1, *a2;
|
||||
SASSERT(n.is_int());
|
||||
rational k;
|
||||
if (m_autil.is_sub(a, a1, a2) && m_autil.is_numeral(a2, k))
|
||||
return expr_ref(m_autil.mk_sub(a1, m_autil.mk_int(k + n)), m());
|
||||
if (m_autil.is_add(a, a1, a2) && m_autil.is_numeral(a2, k))
|
||||
return expr_ref(m_autil.mk_add(a1, m_autil.mk_int(k - n)), m());
|
||||
if (m_autil.is_add(a, a1, a2) && m_autil.is_numeral(a1, k))
|
||||
return expr_ref(m_autil.mk_add(a2, m_autil.mk_int(k - n)), m());
|
||||
return expr_ref(m_autil.mk_sub(a, m_autil.mk_int(n)), m());
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* In general constructs substring(t,1,|t|-1) but if t = substring(s,j,k) then simplifies to substring(s,j+1,k-1)
|
||||
* This method assumes that |t| > 0.
|
||||
*/
|
||||
expr_ref seq_rewriter::mk_seq_rest(expr* t) {
|
||||
expr_ref result(m());
|
||||
expr* s, * j, * k;
|
||||
expr_ref one(m_autil.mk_int(1), m());
|
||||
if (str().is_extract(t, s, j, k))
|
||||
result = str().mk_substr(s, m_autil.mk_add(j, one), m_autil.mk_sub(k, one));
|
||||
else
|
||||
result = str().mk_substr(t, one, m_autil.mk_sub(str().mk_length(t), one));
|
||||
expr* s, * j, * k;
|
||||
rational jv;
|
||||
if (str().is_extract(t, s, j, k) && m_autil.is_numeral(j, jv) && jv >= 0)
|
||||
result = str().mk_substr(s, m_autil.mk_int(jv + 1), mk_sub(k, 1));
|
||||
else
|
||||
result = str().mk_substr(t, one(), mk_sub(str().mk_length(t), 1));
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* In general constructs nth(t,|t|-1) but if t = substring(s,j,k) then simplifies to nth(s,j+k-1)
|
||||
* In general constructs nth(t,|t|-1) but if t = substring(s,j,|s|-j) j >= 0, then simplifies to nth(s,|s|-1)
|
||||
* This method assumes that |t| > 0.
|
||||
*/
|
||||
expr_ref seq_rewriter::mk_seq_last(expr* t) {
|
||||
expr_ref result(m());
|
||||
expr* s, * j, * k;
|
||||
expr_ref one(m_autil.mk_int(1), m());
|
||||
if (str().is_extract(t, s, j, k))
|
||||
result = str().mk_nth_i(s, m_autil.mk_sub(m_autil.mk_add(j, k), one));
|
||||
expr* s, * j, * k, * s_, * len_s;
|
||||
rational jv, i;
|
||||
if (str().is_extract(t, s, j, k) &&
|
||||
m_autil.is_numeral(j, jv) && jv >= 0 &&
|
||||
str().is_len_sub(k, len_s, s_, i) &&
|
||||
s == s_ && jv == i) {
|
||||
expr_ref lastpos = mk_sub(len_s, 1);
|
||||
result = str().mk_nth_i(s, lastpos);
|
||||
}
|
||||
else
|
||||
result = str().mk_nth_i(t, m_autil.mk_sub(str().mk_length(t), one));
|
||||
result = str().mk_nth_i(t, m_autil.mk_sub(str().mk_length(t), one()));
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* In general constructs substring(t,0,|t|-1) but if t = substring(s,j,k) then simplifies to substring(s,j,k-1)
|
||||
* This method assumes that |t| > 0 holds.
|
||||
* In general constructs substring(t,0,|t|-1) but if t = substring(s,0,k) then simplifies to substring(s,0,k-1)
|
||||
* This method assumes that |t| > 0, thus, if t = substring(s,0,k) then k > 0 so substring(s,0,k-1) is correct.
|
||||
*/
|
||||
expr_ref seq_rewriter::mk_seq_butlast(expr* t) {
|
||||
expr_ref result(m());
|
||||
expr* s, * j, * k;
|
||||
expr_ref one(m_autil.mk_int(1), m());
|
||||
if (str().is_extract(t, s, j, k))
|
||||
result = str().mk_substr(s, j, m_autil.mk_sub(k, one));
|
||||
rational v;
|
||||
if (str().is_extract(t, s, j, k) && m_autil.is_numeral(j, v) && v.is_zero()) {
|
||||
expr_ref_vector k_min_1(m());
|
||||
k_min_1.push_back(k);
|
||||
k_min_1.push_back(minus_one());
|
||||
result = str().mk_substr(s, j, m_autil.mk_add_simplify(k_min_1));
|
||||
}
|
||||
else
|
||||
result = str().mk_substr(t, m_autil.mk_int(0), m_autil.mk_sub(str().mk_length(t), one));
|
||||
result = str().mk_substr(t, zero(), m_autil.mk_sub(str().mk_length(t), one()));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -1582,23 +1604,33 @@ br_status seq_rewriter::mk_seq_nth(expr* a, expr* b, expr_ref& result) {
|
|||
result = s;
|
||||
return BR_DONE;
|
||||
}
|
||||
if (str().is_extract(a, s, p, len) && m_autil.is_numeral(p, pos1)) {
|
||||
if (str().is_extract(a, s, p, len) && m_autil.is_numeral(p, pos1) && pos1 > 0) {
|
||||
expr_ref_vector lens(m());
|
||||
rational pos2;
|
||||
/*
|
||||
* nth(s[k, |s| - k], b) =
|
||||
* b < 0 -> nth_u(a, b)
|
||||
* b + k < |s| -> nth(s, b + k)
|
||||
* k >= |s| -> nth_u(empty, b)
|
||||
* k < |s| <= b + k -> nth_u(a, b)
|
||||
*/
|
||||
if (get_lengths(len, lens, pos2) && (pos1 == -pos2) && (lens.size() == 1) && (lens.get(0) == s)) {
|
||||
expr_ref idx(m_autil.mk_int(pos1), m());
|
||||
idx = m_autil.mk_add(b, idx);
|
||||
expr* es[2] = { s, idx };
|
||||
result = m().mk_app(m_util.get_family_id(), OP_SEQ_NTH, 2, es);
|
||||
expr_ref k(m_autil.mk_int(pos1), m());
|
||||
expr_ref case1(str().mk_nth_i(s, m_autil.mk_add(b, k)), m());
|
||||
expr_ref case2(str().mk_nth_u(str().mk_empty(s->get_sort()), b), m());
|
||||
expr_ref case3(str().mk_nth_u(a, b), m());
|
||||
result = case3;
|
||||
result = m().mk_ite(m_autil.mk_lt(m_autil.mk_add(k, b), str().mk_length(s)), case1, result);
|
||||
result = m().mk_ite(m_autil.mk_ge(k, str().mk_length(s)), case2, result);
|
||||
result = m().mk_ite(m_autil.mk_lt(b, zero()), case3, result);
|
||||
return BR_REWRITE_FULL;
|
||||
}
|
||||
}
|
||||
|
||||
expr* es[2] = { a, b};
|
||||
expr* la = str().mk_length(a);
|
||||
result = m().mk_ite(m().mk_and(m_autil.mk_ge(b, zero()), m().mk_not(m_autil.mk_le(la, b))),
|
||||
m().mk_app(m_util.get_family_id(), OP_SEQ_NTH_I, 2, es),
|
||||
m().mk_app(m_util.get_family_id(), OP_SEQ_NTH_U, 2, es));
|
||||
str().mk_nth_i(a, b),
|
||||
str().mk_nth_u(a, b));
|
||||
return BR_REWRITE_FULL;
|
||||
}
|
||||
|
||||
|
@ -1678,7 +1710,7 @@ br_status seq_rewriter::mk_seq_index(expr* a, expr* b, expr* c, expr_ref& result
|
|||
return BR_DONE;
|
||||
}
|
||||
if (m_autil.is_numeral(c, r) && r.is_neg()) {
|
||||
result = m_autil.mk_int(-1);
|
||||
result = minus_one();
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
|
@ -1688,10 +1720,10 @@ br_status seq_rewriter::mk_seq_index(expr* a, expr* b, expr* c, expr_ref& result
|
|||
}
|
||||
|
||||
if (str().is_empty(b)) {
|
||||
result = m().mk_ite(m().mk_and(m_autil.mk_le(m_autil.mk_int(0), c),
|
||||
result = m().mk_ite(m().mk_and(m_autil.mk_le(zero(), c),
|
||||
m_autil.mk_le(c, str().mk_length(a))),
|
||||
c,
|
||||
m_autil.mk_int(-1));
|
||||
minus_one());
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
|
||||
|
@ -2307,7 +2339,7 @@ br_status seq_rewriter::mk_str_to_code(expr* a, expr_ref& result) {
|
|||
if (s.length() == 1)
|
||||
result = m_autil.mk_int(s[0]);
|
||||
else
|
||||
result = m_autil.mk_int(-1);
|
||||
result = minus_one();
|
||||
return BR_DONE;
|
||||
}
|
||||
return BR_FAILED;
|
||||
|
@ -2448,7 +2480,7 @@ br_status seq_rewriter::mk_str_stoi(expr* a, expr_ref& result) {
|
|||
result = m_autil.mk_int(ch - '0');
|
||||
}
|
||||
else {
|
||||
result = m_autil.mk_int(-1);
|
||||
result = minus_one();
|
||||
}
|
||||
return BR_DONE;
|
||||
}
|
||||
|
@ -2456,7 +2488,7 @@ br_status seq_rewriter::mk_str_stoi(expr* a, expr_ref& result) {
|
|||
expr_ref_vector as(m());
|
||||
str().get_concat_units(a, as);
|
||||
if (as.empty()) {
|
||||
result = m_autil.mk_int(-1);
|
||||
result = minus_one();
|
||||
return BR_DONE;
|
||||
}
|
||||
if (str().is_unit(as.back())) {
|
||||
|
@ -2466,11 +2498,11 @@ br_status seq_rewriter::mk_str_stoi(expr* a, expr_ref& result) {
|
|||
expr_ref tail(str().mk_stoi(as.back()), m());
|
||||
expr_ref head(str().mk_concat(as.size() - 1, as.data(), a->get_sort()), m());
|
||||
expr_ref stoi_head(str().mk_stoi(head), m());
|
||||
result = m().mk_ite(m_autil.mk_ge(stoi_head, m_autil.mk_int(0)),
|
||||
result = m().mk_ite(m_autil.mk_ge(stoi_head, zero()),
|
||||
m_autil.mk_add(m_autil.mk_mul(m_autil.mk_int(10), stoi_head), tail),
|
||||
m_autil.mk_int(-1));
|
||||
minus_one());
|
||||
|
||||
result = m().mk_ite(m_autil.mk_ge(tail, m_autil.mk_int(0)),
|
||||
result = m().mk_ite(m_autil.mk_ge(tail, zero()),
|
||||
result,
|
||||
tail);
|
||||
result = m().mk_ite(str().mk_is_empty(head),
|
||||
|
@ -2481,7 +2513,7 @@ br_status seq_rewriter::mk_str_stoi(expr* a, expr_ref& result) {
|
|||
if (str().is_unit(as.get(0), u) && m_util.is_const_char(u, ch) && '0' == ch) {
|
||||
result = str().mk_concat(as.size() - 1, as.data() + 1, as[0]->get_sort());
|
||||
result = m().mk_ite(str().mk_is_empty(result),
|
||||
m_autil.mk_int(0),
|
||||
zero(),
|
||||
str().mk_stoi(result));
|
||||
return BR_REWRITE_FULL;
|
||||
}
|
||||
|
@ -2573,7 +2605,7 @@ bool seq_rewriter::is_sequence(expr* e, expr_ref_vector& seq) {
|
|||
}
|
||||
|
||||
/*
|
||||
s = head + tail where |head| = 1
|
||||
s = [head] + tail where head is the first element of s
|
||||
*/
|
||||
bool seq_rewriter::get_head_tail(expr* s, expr_ref& head, expr_ref& tail) {
|
||||
expr* h = nullptr, *t = nullptr;
|
||||
|
@ -2670,10 +2702,10 @@ expr_ref seq_rewriter::re_predicate(expr* cond, sort* seq_sort) {
|
|||
expr_ref seq_rewriter::is_nullable(expr* r) {
|
||||
STRACE("seq_verbose", tout << "is_nullable: "
|
||||
<< mk_pp(r, m()) << std::endl;);
|
||||
expr_ref result(m_op_cache.find(_OP_RE_IS_NULLABLE, r, nullptr), m());
|
||||
expr_ref result(m_op_cache.find(_OP_RE_IS_NULLABLE, r, nullptr, nullptr), m());
|
||||
if (!result) {
|
||||
result = is_nullable_rec(r);
|
||||
m_op_cache.insert(_OP_RE_IS_NULLABLE, r, nullptr, result);
|
||||
m_op_cache.insert(_OP_RE_IS_NULLABLE, r, nullptr, nullptr, result);
|
||||
}
|
||||
STRACE("seq_verbose", tout << "is_nullable result: "
|
||||
<< result << std::endl;);
|
||||
|
@ -2691,7 +2723,7 @@ expr_ref seq_rewriter::is_nullable_rec(expr* r) {
|
|||
re().is_intersection(r, r1, r2)) {
|
||||
m_br.mk_and(is_nullable(r1), is_nullable(r2), result);
|
||||
}
|
||||
else if (re().is_union(r, r1, r2)) {
|
||||
else if (re().is_union(r, r1, r2) || re().is_antimorov_union(r, r1, r2)) {
|
||||
m_br.mk_or(is_nullable(r1), is_nullable(r2), result);
|
||||
}
|
||||
else if (re().is_diff(r, r1, r2)) {
|
||||
|
@ -2701,6 +2733,7 @@ expr_ref seq_rewriter::is_nullable_rec(expr* r) {
|
|||
else if (re().is_star(r) ||
|
||||
re().is_opt(r) ||
|
||||
re().is_full_seq(r) ||
|
||||
re().is_epsilon(r) ||
|
||||
(re().is_loop(r, r1, lo) && lo == 0) ||
|
||||
(re().is_loop(r, r1, lo, hi) && lo == 0)) {
|
||||
result = m().mk_true();
|
||||
|
@ -2724,7 +2757,7 @@ expr_ref seq_rewriter::is_nullable_rec(expr* r) {
|
|||
result = is_nullable(r1);
|
||||
}
|
||||
else if (m().is_ite(r, cond, r1, r2)) {
|
||||
result = m().mk_ite(cond, is_nullable(r1), is_nullable(r2));
|
||||
m_br.mk_ite(cond, is_nullable(r1), is_nullable(r2), result);
|
||||
}
|
||||
else if (m_util.is_re(r, seq_sort)) {
|
||||
result = is_nullable_symbolic_regex(r, seq_sort);
|
||||
|
@ -2881,7 +2914,8 @@ br_status seq_rewriter::mk_re_reverse(expr* r, expr_ref& result) {
|
|||
br_status seq_rewriter::mk_re_derivative(expr* ele, expr* r, expr_ref& result) {
|
||||
result = mk_derivative(ele, r);
|
||||
// TBD: we may even declare BR_DONE here and potentially miss some simplifications
|
||||
return re().is_derivative(result) ? BR_DONE : BR_REWRITE_FULL;
|
||||
// return re().is_derivative(result) ? BR_DONE : BR_REWRITE_FULL;
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2976,29 +3010,406 @@ bool seq_rewriter::check_deriv_normal_form(expr* r, int level) {
|
|||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
Memoized, recursive implementation of the symbolic derivative such that
|
||||
the result is in normal form.
|
||||
expr_ref seq_rewriter::mk_derivative(expr* r) {
|
||||
sort* seq_sort = nullptr, * ele_sort = nullptr;
|
||||
VERIFY(m_util.is_re(r, seq_sort));
|
||||
VERIFY(m_util.is_seq(seq_sort, ele_sort));
|
||||
expr_ref v(m().mk_var(0, ele_sort), m());
|
||||
return mk_antimirov_deriv(v, r, m().mk_true());
|
||||
}
|
||||
|
||||
Functions without _rec are memoized wrappers, which call the _rec
|
||||
version if lookup fails.
|
||||
|
||||
The main logic is in mk_der_op_rec for combining normal forms.
|
||||
*/
|
||||
expr_ref seq_rewriter::mk_derivative(expr* ele, expr* r) {
|
||||
STRACE("seq_verbose", tout << "derivative: " << mk_pp(ele, m())
|
||||
<< "," << mk_pp(r, m()) << std::endl;);
|
||||
expr_ref result(m_op_cache.find(OP_RE_DERIVATIVE, ele, r), m());
|
||||
return mk_antimirov_deriv(ele, r, m().mk_true());
|
||||
}
|
||||
|
||||
expr_ref seq_rewriter::mk_antimirov_deriv(expr* e, expr* r, expr* path) {
|
||||
// Ensure references are owned
|
||||
expr_ref _e(e, m()), _path(path, m()), _r(r, m());
|
||||
expr_ref result(m_op_cache.find(OP_RE_DERIVATIVE, e, r, path), m());
|
||||
if (!result) {
|
||||
result = mk_derivative_rec(ele, r);
|
||||
m_op_cache.insert(OP_RE_DERIVATIVE, ele, r, result);
|
||||
mk_antimirov_deriv_rec(e, r, path, result);
|
||||
m_op_cache.insert(OP_RE_DERIVATIVE, e, r, path, result);
|
||||
STRACE("seq_regex", tout << "D(" << mk_pp(e, m()) << "," << mk_pp(r, m()) << "," << mk_pp(path, m()) << ")" << std::endl;);
|
||||
STRACE("seq_regex", tout << "= " << mk_pp(result, m()) << std::endl;);
|
||||
}
|
||||
STRACE("seq_verbose", tout << "derivative result: "
|
||||
<< mk_pp(result, m()) << std::endl;);
|
||||
CASSERT("seq_regex", check_deriv_normal_form(r));
|
||||
return result;
|
||||
}
|
||||
|
||||
void seq_rewriter::mk_antimirov_deriv_rec(expr* e, expr* r, expr* path, expr_ref& result) {
|
||||
sort* seq_sort = nullptr, * ele_sort = nullptr;
|
||||
VERIFY(m_util.is_re(r, seq_sort));
|
||||
VERIFY(m_util.is_seq(seq_sort, ele_sort));
|
||||
SASSERT(ele_sort == e->get_sort());
|
||||
expr* r1 = nullptr, * r2 = nullptr, * c = nullptr;
|
||||
expr_ref c1(m());
|
||||
expr_ref c2(m());
|
||||
auto nothing = [&]() { return expr_ref(re().mk_empty(r->get_sort()), m()); };
|
||||
auto epsilon = [&]() { return expr_ref(re().mk_epsilon(seq_sort), m()); };
|
||||
auto dotstar = [&]() { return expr_ref(re().mk_full_seq(r->get_sort()), m()); };
|
||||
unsigned lo = 0, hi = 0;
|
||||
if (re().is_empty(r) || re().is_epsilon(r))
|
||||
// D(e,[]) = D(e,()) = []
|
||||
result = nothing();
|
||||
else if (re().is_full_seq(r) || re().is_dot_plus(r))
|
||||
// D(e,.*) = D(e,.+) = .*
|
||||
result = dotstar();
|
||||
else if (re().is_full_char(r))
|
||||
// D(e,.) = ()
|
||||
result = epsilon();
|
||||
else if (re().is_to_re(r, r1)) {
|
||||
expr_ref h(m());
|
||||
expr_ref t(m());
|
||||
// here r1 is a sequence
|
||||
if (get_head_tail(r1, h, t)) {
|
||||
if (eq_char(e, h))
|
||||
result = re().mk_to_re(t);
|
||||
else if (neq_char(e, h))
|
||||
result = nothing();
|
||||
else
|
||||
result = re().mk_ite_simplify(m().mk_eq(e, h), re().mk_to_re(t), nothing());
|
||||
}
|
||||
else {
|
||||
// observe that the precondition |r1|>0 is is implied by c1 for use of mk_seq_first
|
||||
m_br.mk_and(m().mk_not(m().mk_eq(r1, str().mk_empty(seq_sort))), m().mk_eq(mk_seq_first(r1), e), c1);
|
||||
m_br.mk_and(path, c1, c2);
|
||||
if (m().is_false(c2))
|
||||
result = nothing();
|
||||
else
|
||||
// observe that the precondition |r1|>0 is implied by c1 for use of mk_seq_rest
|
||||
result = m().mk_ite(c1, re().mk_to_re(mk_seq_rest(r1)), nothing());
|
||||
}
|
||||
}
|
||||
else if (re().is_reverse(r, r2))
|
||||
if (re().is_to_re(r2, r1)) {
|
||||
// here r1 is a sequence
|
||||
// observe that the precondition |r1|>0 of mk_seq_last is implied by c1
|
||||
m_br.mk_and(m().mk_not(m().mk_eq(r1, str().mk_empty(seq_sort))), m().mk_eq(mk_seq_last(r1), e), c1);
|
||||
m_br.mk_and(path, c1, c2);
|
||||
if (m().is_false(c2))
|
||||
result = nothing();
|
||||
else
|
||||
// observe that the precondition |r1|>0 of mk_seq_rest is implied by c1
|
||||
result = re().mk_ite_simplify(c1, re().mk_reverse(re().mk_to_re(mk_seq_butlast(r1))), nothing());
|
||||
}
|
||||
else {
|
||||
result = mk_regex_reverse(r2);
|
||||
if (result.get() == r)
|
||||
//r2 is an uninterpreted regex that is stuck
|
||||
//for example if r = (re.reverse R) where R is a regex variable then
|
||||
//here result.get() == r
|
||||
result = re().mk_derivative(e, result);
|
||||
else
|
||||
result = mk_antimirov_deriv(e, result, path);
|
||||
}
|
||||
else if (re().is_concat(r, r1, r2)) {
|
||||
expr_ref r1nullable(is_nullable(r1), m());
|
||||
c1 = mk_antimirov_deriv_concat(mk_antimirov_deriv(e, r1, path), r2);
|
||||
expr_ref r1nullable_and_path(m());
|
||||
m_br.mk_and(r1nullable, path, r1nullable_and_path);
|
||||
if (m().is_false(r1nullable_and_path))
|
||||
// D(e,r1)r2
|
||||
result = c1;
|
||||
else
|
||||
// D(e,r1)r2|(ite (r1nullable) (D(e,r2)) [])
|
||||
// observe that (mk_ite_simplify(true, D(e,r2), []) = D(e,r2)
|
||||
result = mk_antimirov_deriv_union(c1, re().mk_ite_simplify(r1nullable, mk_antimirov_deriv(e, r2, path), nothing()));
|
||||
}
|
||||
else if (m().is_ite(r, c, r1, r2)) {
|
||||
c1 = simplify_path(m().mk_and(c, path));
|
||||
c2 = simplify_path(m().mk_and(m().mk_not(c), path));
|
||||
if (m().is_false(c1))
|
||||
result = mk_antimirov_deriv(e, r2, c2);
|
||||
else if (m().is_false(c2))
|
||||
result = mk_antimirov_deriv(e, r1, c1);
|
||||
else
|
||||
result = re().mk_ite_simplify(c, mk_antimirov_deriv(e, r1, c1), mk_antimirov_deriv(e, r2, c2));
|
||||
}
|
||||
else if (re().is_range(r, r1, r2)) {
|
||||
expr_ref range(m());
|
||||
expr_ref psi(m().mk_false(), m());
|
||||
if (str().is_unit_string(r1, c1) && str().is_unit_string(r2, c2)) {
|
||||
SASSERT(u().is_char(c1));
|
||||
SASSERT(u().is_char(c2));
|
||||
// case: c1 <= e <= c2
|
||||
range = simplify_path(m().mk_and(u().mk_le(c1, e), u().mk_le(e, c2)));
|
||||
psi = simplify_path(m().mk_and(path, range));
|
||||
}
|
||||
else if (!str().is_string(r1) && str().is_unit_string(r2, c2)) {
|
||||
SASSERT(u().is_char(c2));
|
||||
// r1 nonground: |r1|=1 & r1[0] <= e <= c2
|
||||
expr_ref one(m_autil.mk_int(1), m());
|
||||
expr_ref zero(m_autil.mk_int(0), m());
|
||||
expr_ref r1_length_eq_one(m().mk_eq(str().mk_length(r1), one), m());
|
||||
expr_ref r1_0(str().mk_nth_i(r1, zero), m());
|
||||
range = simplify_path(m().mk_and(r1_length_eq_one, m().mk_and(u().mk_le(r1_0, e), u().mk_le(e, c2))));
|
||||
psi = simplify_path(m().mk_and(path, range));
|
||||
}
|
||||
else if (!str().is_string(r2) && str().is_unit_string(r1, c1)) {
|
||||
SASSERT(u().is_char(c1));
|
||||
// r2 nonground: |r2|=1 & c1 <= e <= r2_0
|
||||
expr_ref one(m_autil.mk_int(1), m());
|
||||
expr_ref zero(m_autil.mk_int(0), m());
|
||||
expr_ref r2_length_eq_one(m().mk_eq(str().mk_length(r2), one), m());
|
||||
expr_ref r2_0(str().mk_nth_i(r2, zero), m());
|
||||
range = simplify_path(m().mk_and(r2_length_eq_one, m().mk_and(u().mk_le(c1, e), u().mk_le(e, r2_0))));
|
||||
psi = simplify_path(m().mk_and(path, range));
|
||||
}
|
||||
else if (!str().is_string(r1) && !str().is_string(r2)) {
|
||||
// both r1 and r2 nonground: |r1|=1 & |r2|=1 & r1[0] <= e <= r2[0]
|
||||
expr_ref one(m_autil.mk_int(1), m());
|
||||
expr_ref zero(m_autil.mk_int(0), m());
|
||||
expr_ref r1_length_eq_one(m().mk_eq(str().mk_length(r1), one), m());
|
||||
expr_ref r1_0(str().mk_nth_i(r1, zero), m());
|
||||
expr_ref r2_length_eq_one(m().mk_eq(str().mk_length(r2), one), m());
|
||||
expr_ref r2_0(str().mk_nth_i(r2, zero), m());
|
||||
range = simplify_path(m().mk_and(r1_length_eq_one, m().mk_and(r2_length_eq_one, m().mk_and(u().mk_le(r1_0, e), u().mk_le(e, r2_0)))));
|
||||
psi = simplify_path(m().mk_and(path, range));
|
||||
}
|
||||
if (m().is_false(psi))
|
||||
result = nothing();
|
||||
else
|
||||
result = re().mk_ite_simplify(range, epsilon(), nothing());
|
||||
}
|
||||
else if (re().is_union(r, r1, r2))
|
||||
result = mk_antimirov_deriv_union(mk_antimirov_deriv(e, r1, path), mk_antimirov_deriv(e, r2, path));
|
||||
else if (re().is_intersection(r, r1, r2))
|
||||
result = mk_antimirov_deriv_intersection(
|
||||
mk_antimirov_deriv(e, r1, path),
|
||||
mk_antimirov_deriv(e, r2, path), m().mk_true());
|
||||
else if (re().is_star(r, r1) || re().is_plus(r, r1) || (re().is_loop(r, r1, lo) && 0 <= lo && lo <= 1))
|
||||
result = mk_antimirov_deriv_concat(mk_antimirov_deriv(e, r1, path), re().mk_star(r1));
|
||||
else if (re().is_loop(r, r1, lo))
|
||||
result = mk_antimirov_deriv_concat(mk_antimirov_deriv(e, r1, path), re().mk_loop(r1, lo - 1));
|
||||
else if (re().is_loop(r, r1, lo, hi)) {
|
||||
if ((lo == 0 && hi == 0) || hi < lo)
|
||||
result = nothing();
|
||||
else
|
||||
result = mk_antimirov_deriv_concat(mk_antimirov_deriv(e, r1, path), re().mk_loop(r1, (lo == 0 ? 0 : lo - 1), hi - 1));
|
||||
}
|
||||
else if (re().is_opt(r, r1))
|
||||
result = mk_antimirov_deriv(e, r1, path);
|
||||
else if (re().is_complement(r, r1))
|
||||
// D(e,~r1) = ~D(e,r1)
|
||||
result = mk_antimirov_deriv_negate(mk_antimirov_deriv(e, r1, path));
|
||||
else if (re().is_diff(r, r1, r2))
|
||||
result = mk_antimirov_deriv_intersection(
|
||||
mk_antimirov_deriv(e, r1, path),
|
||||
mk_antimirov_deriv_negate(mk_antimirov_deriv(e, r2, path)), m().mk_true());
|
||||
else if (re().is_of_pred(r, r1)) {
|
||||
array_util array(m());
|
||||
expr* args[2] = { r1, e };
|
||||
result = array.mk_select(2, args);
|
||||
// Use mk_der_cond to normalize
|
||||
result = mk_der_cond(result, e, seq_sort);
|
||||
}
|
||||
else
|
||||
// stuck cases
|
||||
result = re().mk_derivative(e, r);
|
||||
}
|
||||
|
||||
expr_ref seq_rewriter::mk_antimirov_deriv_intersection(expr* d1, expr* d2, expr* path) {
|
||||
sort* seq_sort = nullptr, * ele_sort = nullptr;
|
||||
VERIFY(m_util.is_re(d1, seq_sort));
|
||||
VERIFY(m_util.is_seq(seq_sort, ele_sort));
|
||||
expr_ref result(m());
|
||||
expr* c, * a, * b;
|
||||
if (d1 == d2 || re().is_full_seq(d2) || re().is_empty(d1))
|
||||
result = d1;
|
||||
else if (re().is_full_seq(d1) || re().is_empty(d2))
|
||||
result = d2;
|
||||
else if (m().is_ite(d1, c, a, b)) {
|
||||
expr_ref path_and_c(simplify_path(m().mk_and(path, c)), m());
|
||||
expr_ref path_and_notc(simplify_path(m().mk_and(path, m().mk_not(c))), m());
|
||||
if (m().is_false(path_and_c))
|
||||
result = mk_antimirov_deriv_intersection(b, d2, path);
|
||||
else if (m().is_false(path_and_notc))
|
||||
result = mk_antimirov_deriv_intersection(a, d2, path);
|
||||
else
|
||||
result = m().mk_ite(c, mk_antimirov_deriv_intersection(a, d2, path_and_c),
|
||||
mk_antimirov_deriv_intersection(b, d2, path_and_notc));
|
||||
}
|
||||
else if (m().is_ite(d2))
|
||||
// swap d1 and d2
|
||||
result = mk_antimirov_deriv_intersection(d2, d1, path);
|
||||
else if (re().is_union(d1, a, b))
|
||||
// distribute intersection over the union in d1
|
||||
result = mk_antimirov_deriv_union(mk_antimirov_deriv_intersection(a, d2, path), mk_antimirov_deriv_intersection(b, d2, path));
|
||||
else if (re().is_union(d2, a, b))
|
||||
// distribute intersection over the union in d2
|
||||
result = mk_antimirov_deriv_union(mk_antimirov_deriv_intersection(d1, a, path), mk_antimirov_deriv_intersection(d1, b, path));
|
||||
else
|
||||
// in all other cases create the intersection regex
|
||||
// TODO: flatten, order and merge d1 and d2 to maintain equality under similarity
|
||||
result = (d1->get_id() <= d2->get_id() ? re().mk_inter(d1, d2) : re().mk_inter(d2, d1));
|
||||
return result;
|
||||
}
|
||||
|
||||
expr_ref seq_rewriter::mk_antimirov_deriv_concat(expr* d, expr* r) {
|
||||
expr_ref result(m());
|
||||
// Take reference count of r and d
|
||||
expr_ref _r(r, m()), _d(d, m());
|
||||
expr* c, * t, * e;
|
||||
if (m().is_ite(d, c, t, e))
|
||||
result = m().mk_ite(c, mk_antimirov_deriv_concat(t, r), mk_antimirov_deriv_concat(e, r));
|
||||
else if (re().is_union(d, t, e))
|
||||
result = re().mk_union(mk_antimirov_deriv_concat(t, r), mk_antimirov_deriv_concat(e, r));
|
||||
else
|
||||
result = mk_re_append(d, r);
|
||||
return result;
|
||||
}
|
||||
|
||||
expr_ref seq_rewriter::mk_antimirov_deriv_negate(expr* d) {
|
||||
sort* seq_sort = nullptr;
|
||||
VERIFY(m_util.is_re(d, seq_sort));
|
||||
auto nothing = [&]() { return expr_ref(re().mk_empty(d->get_sort()), m()); };
|
||||
auto epsilon = [&]() { return expr_ref(re().mk_epsilon(seq_sort), m()); };
|
||||
auto dotstar = [&]() { return expr_ref(re().mk_full_seq(d->get_sort()), m()); };
|
||||
auto dotplus = [&]() { return expr_ref(re().mk_plus(re().mk_full_char(d->get_sort())), m()); };
|
||||
expr_ref result(m());
|
||||
expr* c, * t, * e;
|
||||
if (re().is_empty(d))
|
||||
result = dotstar();
|
||||
else if (re().is_epsilon(d))
|
||||
result = dotplus();
|
||||
else if (re().is_full_seq(d))
|
||||
result = nothing();
|
||||
else if (re().is_dot_plus(d))
|
||||
result = epsilon();
|
||||
else if (m().is_ite(d, c, t, e))
|
||||
result = m().mk_ite(c, mk_antimirov_deriv_negate(t), mk_antimirov_deriv_negate(e));
|
||||
else if (re().is_union(d, t, e))
|
||||
result = re().mk_inter(mk_antimirov_deriv_negate(t), mk_antimirov_deriv_negate(e));
|
||||
else if (re().is_intersection(d, t, e))
|
||||
result = re().mk_union(mk_antimirov_deriv_negate(t), mk_antimirov_deriv_negate(e));
|
||||
else if (re().is_complement(d, t))
|
||||
result = t;
|
||||
else
|
||||
result = re().mk_complement(d);
|
||||
return result;
|
||||
}
|
||||
|
||||
expr_ref seq_rewriter::mk_antimirov_deriv_union(expr* d1, expr* d2) {
|
||||
expr_ref result(m());
|
||||
if (re().is_empty(d1) || re().is_full_seq(d2))
|
||||
result = d2;
|
||||
else if (re().is_empty(d2) || re().is_full_seq(d1))
|
||||
result = d1;
|
||||
else if (re().is_dot_plus(d1) && re().get_info(d2).min_length > 0)
|
||||
result = d1;
|
||||
else if (re().is_dot_plus(d2) && re().get_info(d1).min_length > 0)
|
||||
result = d2;
|
||||
else
|
||||
// TODO: flatten, order and merge d1 and d2 to maintain equality under similarity
|
||||
result = (d1->get_id() <= d2->get_id() ? re().mk_union(d1, d2) : re().mk_union(d2, d1));
|
||||
return result;
|
||||
}
|
||||
|
||||
expr_ref seq_rewriter::mk_regex_reverse(expr* r) {
|
||||
expr* r1 = nullptr, * r2 = nullptr, * c = nullptr;
|
||||
unsigned lo = 0, hi = 0;
|
||||
expr_ref result(m());
|
||||
if (re().is_empty(r) || re().is_range(r) || re().is_epsilon(r) || re().is_full_seq(r) ||
|
||||
re().is_full_char(r) || re().is_dot_plus(r) || re().is_of_pred(r))
|
||||
result = r;
|
||||
else if (re().is_to_re(r))
|
||||
result = re().mk_reverse(r);
|
||||
else if (re().is_reverse(r, r1))
|
||||
result = r1;
|
||||
else if (re().is_concat(r, r1, r2))
|
||||
result = mk_regex_concat(mk_regex_reverse(r2), mk_regex_reverse(r1));
|
||||
else if (m().is_ite(r, c, r1, r2))
|
||||
result = m().mk_ite(c, mk_regex_reverse(r1), mk_regex_reverse(r2));
|
||||
else if (re().is_union(r, r1, r2))
|
||||
result = re().mk_union(mk_regex_reverse(r1), mk_regex_reverse(r2));
|
||||
else if (re().is_intersection(r, r1, r2))
|
||||
result = re().mk_inter(mk_regex_reverse(r1), mk_regex_reverse(r2));
|
||||
else if (re().is_diff(r, r1, r2))
|
||||
result = re().mk_diff(mk_regex_reverse(r1), mk_regex_reverse(r2));
|
||||
else if (re().is_star(r, r1))
|
||||
result = re().mk_star(mk_regex_reverse(r1));
|
||||
else if (re().is_plus(r, r1))
|
||||
result = re().mk_plus(mk_regex_reverse(r1));
|
||||
else if (re().is_loop(r, r1, lo))
|
||||
result = re().mk_loop(mk_regex_reverse(r1), lo);
|
||||
else if (re().is_loop(r, r1, lo, hi))
|
||||
result = re().mk_loop(mk_regex_reverse(r1), lo, hi);
|
||||
else if (re().is_opt(r, r1))
|
||||
result = re().mk_opt(mk_regex_reverse(r1));
|
||||
else if (re().is_complement(r, r1))
|
||||
result = re().mk_complement(mk_regex_reverse(r1));
|
||||
else
|
||||
//stuck cases: such as r being a regex variable
|
||||
//observe that re().mk_reverse(to_re(s)) is not a stuck case
|
||||
result = re().mk_reverse(r);
|
||||
return result;
|
||||
}
|
||||
|
||||
expr_ref seq_rewriter::mk_regex_concat(expr* r, expr* s) {
|
||||
sort* seq_sort = nullptr;
|
||||
VERIFY(m_util.is_re(r, seq_sort));
|
||||
SASSERT(r->get_sort() == s->get_sort());
|
||||
expr_ref result(m());
|
||||
expr* r1, * r2;
|
||||
if (re().is_epsilon(r) || re().is_empty(s))
|
||||
result = s;
|
||||
else if (re().is_epsilon(s) || re().is_empty(r))
|
||||
result = r;
|
||||
else if (re().is_full_seq(r) && re().is_full_seq(s))
|
||||
result = r;
|
||||
else if (re().is_concat(r, r1, r2))
|
||||
//create the resulting concatenation in right-associative form
|
||||
result = mk_regex_concat(r1, mk_regex_concat(r2, s));
|
||||
else {
|
||||
//TODO: perhaps simplifiy some further cases such as .*. = ..* = .*.+ = .+.* = .+
|
||||
result = re().mk_concat(r, s);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
expr_ref seq_rewriter::mk_in_antimirov(expr* s, expr* d){
|
||||
expr_ref result(mk_in_antimirov_rec(s, d), m());
|
||||
return result;
|
||||
}
|
||||
|
||||
expr_ref seq_rewriter::mk_in_antimirov_rec(expr* s, expr* d) {
|
||||
expr* c, * d1, * d2;
|
||||
expr_ref result(m());
|
||||
if (re().is_full_seq(d) || (str().min_length(s) > 0 && re().is_dot_plus(d)))
|
||||
// s in .* <==> true, also: s in .+ <==> true when |s|>0
|
||||
result = m().mk_true();
|
||||
else if (re().is_empty(d) || (str().min_length(s) > 0 && re().is_epsilon(d)))
|
||||
// s in [] <==> false, also: s in () <==> false when |s|>0
|
||||
result = m().mk_false();
|
||||
else if (m().is_ite(d, c, d1, d2))
|
||||
result = re().mk_ite_simplify(c, mk_in_antimirov_rec(s, d1), mk_in_antimirov_rec(s, d2));
|
||||
else if (re().is_union(d, d1, d2))
|
||||
m_br.mk_or(mk_in_antimirov_rec(s, d1), mk_in_antimirov_rec(s, d2), result);
|
||||
else
|
||||
result = re().mk_in_re(s, d);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
path is typically a conjunction of (negated) character equations or constraints that can potentially be simplified
|
||||
the first element of each equation is assumed to be the element parameter, for example x = (:var 0),
|
||||
for example a constraint x='a' & x='b' is simplified to false
|
||||
*/
|
||||
expr_ref seq_rewriter::simplify_path(expr* path) {
|
||||
//TODO: more systematic simplifications
|
||||
expr_ref result(path, m());
|
||||
expr* h = nullptr, * t = nullptr, * lhs = nullptr, * rhs = nullptr, * h1 = nullptr;
|
||||
if (m().is_and(path, h, t)) {
|
||||
if (m().is_true(h))
|
||||
result = simplify_path(t);
|
||||
else if (m().is_true(t))
|
||||
result = simplify_path(h);
|
||||
else if (m().is_eq(h, lhs, rhs) || (m().is_not(h, h1) && m().is_eq(h1, lhs, rhs)))
|
||||
elim_condition(lhs, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
expr_ref seq_rewriter::mk_der_antimorov_union(expr* r1, expr* r2) {
|
||||
return mk_der_op(_OP_RE_ANTIMOROV_UNION, r1, r2);
|
||||
}
|
||||
|
@ -3016,7 +3427,7 @@ expr_ref seq_rewriter::mk_der_concat(expr* r1, expr* r2) {
|
|||
}
|
||||
|
||||
/*
|
||||
Utility functions to decide char <, ==, and <=.
|
||||
Utility functions to decide char <, ==, !=, and <=.
|
||||
Return true if deduced, false if unknown.
|
||||
*/
|
||||
bool seq_rewriter::lt_char(expr* ch1, expr* ch2) {
|
||||
|
@ -3027,6 +3438,11 @@ bool seq_rewriter::lt_char(expr* ch1, expr* ch2) {
|
|||
bool seq_rewriter::eq_char(expr* ch1, expr* ch2) {
|
||||
return ch1 == ch2;
|
||||
}
|
||||
bool seq_rewriter::neq_char(expr* ch1, expr* ch2) {
|
||||
unsigned u1, u2;
|
||||
return u().is_const_char(ch1, u1) &&
|
||||
u().is_const_char(ch2, u2) && (u1 != u2);
|
||||
}
|
||||
bool seq_rewriter::le_char(expr* ch1, expr* ch2) {
|
||||
return eq_char(ch1, ch2) || lt_char(ch1, ch2);
|
||||
}
|
||||
|
@ -3257,10 +3673,10 @@ expr_ref seq_rewriter::mk_der_op(decl_kind k, expr* a, expr* b) {
|
|||
default:
|
||||
break;
|
||||
}
|
||||
result = m_op_cache.find(k, a, b);
|
||||
result = m_op_cache.find(k, a, b, nullptr);
|
||||
if (!result) {
|
||||
result = mk_der_op_rec(k, a, b);
|
||||
m_op_cache.insert(k, a, b, result);
|
||||
m_op_cache.insert(k, a, b, nullptr, result);
|
||||
}
|
||||
CASSERT("seq_regex", check_deriv_normal_form(result));
|
||||
return result;
|
||||
|
@ -3269,7 +3685,7 @@ expr_ref seq_rewriter::mk_der_op(decl_kind k, expr* a, expr* b) {
|
|||
expr_ref seq_rewriter::mk_der_compl(expr* r) {
|
||||
STRACE("seq_verbose", tout << "mk_der_compl: " << mk_pp(r, m())
|
||||
<< std::endl;);
|
||||
expr_ref result(m_op_cache.find(OP_RE_COMPLEMENT, r, nullptr), m());
|
||||
expr_ref result(m_op_cache.find(OP_RE_COMPLEMENT, r, nullptr, nullptr), m());
|
||||
if (!result) {
|
||||
expr* c = nullptr, * r1 = nullptr, * r2 = nullptr;
|
||||
if (re().is_antimorov_union(r, r1, r2)) {
|
||||
|
@ -3285,7 +3701,7 @@ expr_ref seq_rewriter::mk_der_compl(expr* r) {
|
|||
}
|
||||
else if (BR_FAILED == mk_re_complement(r, result))
|
||||
result = re().mk_complement(r);
|
||||
m_op_cache.insert(OP_RE_COMPLEMENT, r, nullptr, result);
|
||||
m_op_cache.insert(OP_RE_COMPLEMENT, r, nullptr, nullptr, result);
|
||||
}
|
||||
CASSERT("seq_regex", check_deriv_normal_form(result));
|
||||
return result;
|
||||
|
@ -3509,7 +3925,7 @@ expr_ref seq_rewriter::mk_derivative_rec(expr* ele, expr* r) {
|
|||
// construct the term (if (r2 != () and (ele = (last r2)) then reverse(to_re (butlast r2)) else []))
|
||||
// hd = first of reverse(r2) i.e. last of r2
|
||||
// tl = rest of reverse(r2) i.e. butlast of r2
|
||||
//hd = str().mk_nth_i(r2, m_autil.mk_sub(str().mk_length(r2), m_autil.mk_int(1)));
|
||||
//hd = str().mk_nth_i(r2, m_autil.mk_sub(str().mk_length(r2), one()));
|
||||
hd = mk_seq_last(r2);
|
||||
m_br.mk_and(m().mk_not(m().mk_eq(r2, str().mk_empty(seq_sort))), m().mk_eq(hd, ele), result);
|
||||
tl = re().mk_to_re(mk_seq_butlast(r2));
|
||||
|
@ -3537,9 +3953,9 @@ expr_ref seq_rewriter::mk_derivative_rec(expr* ele, expr* r) {
|
|||
return mk_empty();
|
||||
}
|
||||
}
|
||||
expr* e1 = nullptr, *e2 = nullptr;
|
||||
expr* e1 = nullptr, * e2 = nullptr;
|
||||
if (str().is_unit(r1, e1) && str().is_unit(r2, e2)) {
|
||||
SASSERT(u().is_char(e1));
|
||||
SASSERT(u().is_char(e1));
|
||||
// Use mk_der_cond to normalize
|
||||
STRACE("seq_verbose", tout << "deriv range str" << std::endl;);
|
||||
expr_ref p1(u().mk_le(e1, ele), m());
|
||||
|
@ -3760,7 +4176,7 @@ br_status seq_rewriter::mk_str_in_regexp(expr* a, expr* b, expr_ref& result) {
|
|||
(re().is_union(b, b1, eps) && re().is_epsilon(eps)) ||
|
||||
(re().is_union(b, eps, b1) && re().is_epsilon(eps)))
|
||||
{
|
||||
result = m().mk_ite(m().mk_eq(str().mk_length(a), m_autil.mk_int(0)),
|
||||
result = m().mk_ite(m().mk_eq(str().mk_length(a), zero()),
|
||||
m().mk_true(),
|
||||
re().mk_in_re(a, b1));
|
||||
return BR_REWRITE_FULL;
|
||||
|
@ -3775,8 +4191,10 @@ br_status seq_rewriter::mk_str_in_regexp(expr* a, expr* b, expr_ref& result) {
|
|||
|
||||
expr_ref hd(m()), tl(m());
|
||||
if (get_head_tail(a, hd, tl)) {
|
||||
result = re().mk_in_re(tl, re().mk_derivative(hd, b));
|
||||
return BR_REWRITE2;
|
||||
//result = re().mk_in_re(tl, re().mk_derivative(hd, b));
|
||||
//result = re().mk_in_re(tl, mk_derivative(hd, b));
|
||||
result = mk_in_antimirov(tl, mk_antimirov_deriv(hd, b, m().mk_true()));
|
||||
return BR_REWRITE_FULL;
|
||||
}
|
||||
|
||||
if (get_head_tail_reversed(a, hd, tl)) {
|
||||
|
@ -3791,7 +4209,7 @@ br_status seq_rewriter::mk_str_in_regexp(expr* a, expr* b, expr_ref& result) {
|
|||
expr_ref len_a(str().mk_length(a), m());
|
||||
expr_ref len_tl(m_autil.mk_sub(len_a, len_hd), m());
|
||||
result = m().mk_and(m_autil.mk_ge(len_a, len_hd),
|
||||
re().mk_in_re(str().mk_substr(a, m_autil.mk_int(0), len_hd), hd),
|
||||
re().mk_in_re(str().mk_substr(a, zero(), len_hd), hd),
|
||||
re().mk_in_re(str().mk_substr(a, len_hd, len_tl), tl));
|
||||
return BR_REWRITE_FULL;
|
||||
}
|
||||
|
@ -3802,7 +4220,7 @@ br_status seq_rewriter::mk_str_in_regexp(expr* a, expr* b, expr_ref& result) {
|
|||
expr_ref len_hd(m_autil.mk_sub(len_a, len_tl), m());
|
||||
expr* s = nullptr;
|
||||
result = m().mk_and(m_autil.mk_ge(len_a, len_tl),
|
||||
re().mk_in_re(str().mk_substr(a, m_autil.mk_int(0), len_hd), hd),
|
||||
re().mk_in_re(str().mk_substr(a, zero(), len_hd), hd),
|
||||
(re().is_to_re(tl, s) ? m().mk_eq(s, str().mk_substr(a, len_hd, len_tl)) :
|
||||
re().mk_in_re(str().mk_substr(a, len_hd, len_tl), tl)));
|
||||
return BR_REWRITE_FULL;
|
||||
|
@ -3912,6 +4330,10 @@ br_status seq_rewriter::mk_re_concat(expr* a, expr* b, expr_ref& result) {
|
|||
return BR_REWRITE2;
|
||||
}
|
||||
expr* a1 = nullptr, *b1 = nullptr;
|
||||
if (re().is_to_re(a, a1) && re().is_to_re(b, b1)) {
|
||||
result = re().mk_to_re(str().mk_concat(a1, b1));
|
||||
return BR_DONE;
|
||||
}
|
||||
if (re().is_star(a, a1) && re().is_star(b, b1) && a1 == b1) {
|
||||
result = a;
|
||||
return BR_DONE;
|
||||
|
@ -5151,15 +5573,15 @@ bool seq_rewriter::reduce_eq_empty(expr* l, expr* r, expr_ref& result) {
|
|||
if (str().is_extract(r, s, offset, len)) {
|
||||
expr_ref len_s(str().mk_length(s), m());
|
||||
expr_ref_vector fmls(m());
|
||||
fmls.push_back(m_autil.mk_lt(offset, m_autil.mk_int(0)));
|
||||
fmls.push_back(m_autil.mk_lt(offset, zero()));
|
||||
fmls.push_back(m().mk_eq(s, l));
|
||||
fmls.push_back(m_autil.mk_le(len, m_autil.mk_int(0)));
|
||||
fmls.push_back(m_autil.mk_le(len, zero()));
|
||||
fmls.push_back(m_autil.mk_le(len_s, offset));
|
||||
result = m().mk_or(fmls);
|
||||
return true;
|
||||
}
|
||||
if (str().is_itos(r, s)) {
|
||||
result = m_autil.mk_lt(s, m_autil.mk_int(0));
|
||||
result = m_autil.mk_lt(s, zero());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -5275,19 +5697,20 @@ seq_rewriter::op_cache::op_cache(ast_manager& m):
|
|||
m_trail(m)
|
||||
{}
|
||||
|
||||
expr* seq_rewriter::op_cache::find(decl_kind op, expr* a, expr* b) {
|
||||
op_entry e(op, a, b, nullptr);
|
||||
expr* seq_rewriter::op_cache::find(decl_kind op, expr* a, expr* b, expr* c) {
|
||||
op_entry e(op, a, b, c, nullptr);
|
||||
m_table.find(e, e);
|
||||
|
||||
return e.r;
|
||||
}
|
||||
|
||||
void seq_rewriter::op_cache::insert(decl_kind op, expr* a, expr* b, expr* r) {
|
||||
void seq_rewriter::op_cache::insert(decl_kind op, expr* a, expr* b, expr* c, expr* r) {
|
||||
cleanup();
|
||||
if (a) m_trail.push_back(a);
|
||||
if (b) m_trail.push_back(b);
|
||||
if (c) m_trail.push_back(c);
|
||||
if (r) m_trail.push_back(r);
|
||||
m_table.insert(op_entry(op, a, b, r));
|
||||
m_table.insert(op_entry(op, a, b, c, r));
|
||||
}
|
||||
|
||||
void seq_rewriter::op_cache::cleanup() {
|
||||
|
|
|
@ -117,20 +117,20 @@ class seq_rewriter {
|
|||
class op_cache {
|
||||
struct op_entry {
|
||||
decl_kind k;
|
||||
expr* a, *b, *r;
|
||||
op_entry(decl_kind k, expr* a, expr* b, expr* r): k(k), a(a), b(b), r(r) {}
|
||||
op_entry():k(0), a(nullptr), b(nullptr), r(nullptr) {}
|
||||
expr* a, *b, *c, *r;
|
||||
op_entry(decl_kind k, expr* a, expr* b, expr* c, expr* r): k(k), a(a), b(b), c(c), r(r) {}
|
||||
op_entry():k(0), a(nullptr), b(nullptr), c(nullptr), r(nullptr) {}
|
||||
};
|
||||
|
||||
struct hash_entry {
|
||||
unsigned operator()(op_entry const& e) const {
|
||||
return mk_mix(e.k, e.a ? e.a->get_id() : 0, e.b ? e.b->get_id() : 0);
|
||||
return combine_hash(mk_mix(e.k, e.a ? e.a->get_id() : 0, e.b ? e.b->get_id() : 0), e.c ? e.c->get_id() : 0);
|
||||
}
|
||||
};
|
||||
|
||||
struct eq_entry {
|
||||
bool operator()(op_entry const& a, op_entry const& b) const {
|
||||
return a.k == b.k && a.a == b.a && a.b == b.b;
|
||||
bool operator()(op_entry const& a, op_entry const& b) const {
|
||||
return a.k == b.k && a.a == b.a && a.b == b.b && a.c == b.c;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -143,8 +143,8 @@ class seq_rewriter {
|
|||
|
||||
public:
|
||||
op_cache(ast_manager& m);
|
||||
expr* find(decl_kind op, expr* a, expr* b);
|
||||
void insert(decl_kind op, expr* a, expr* b, expr* r);
|
||||
expr* find(decl_kind op, expr* a, expr* b, expr* c);
|
||||
void insert(decl_kind op, expr* a, expr* b, expr* c, expr* r);
|
||||
};
|
||||
|
||||
seq_util m_util;
|
||||
|
@ -208,8 +208,24 @@ class seq_rewriter {
|
|||
bool check_deriv_normal_form(expr* r, int level = 3);
|
||||
#endif
|
||||
|
||||
void mk_antimirov_deriv_rec(expr* e, expr* r, expr* path, expr_ref& result);
|
||||
|
||||
expr_ref mk_antimirov_deriv(expr* e, expr* r, expr* path);
|
||||
expr_ref mk_in_antimirov_rec(expr* s, expr* d);
|
||||
expr_ref mk_in_antimirov(expr* s, expr* d);
|
||||
|
||||
expr_ref mk_antimirov_deriv_intersection(expr* d1, expr* d2, expr* path);
|
||||
expr_ref mk_antimirov_deriv_concat(expr* d, expr* r);
|
||||
expr_ref mk_antimirov_deriv_negate(expr* d);
|
||||
expr_ref mk_antimirov_deriv_union(expr* d1, expr* d2);
|
||||
expr_ref mk_regex_reverse(expr* r);
|
||||
expr_ref mk_regex_concat(expr* r1, expr* r2);
|
||||
|
||||
expr_ref simplify_path(expr* path);
|
||||
|
||||
bool lt_char(expr* ch1, expr* ch2);
|
||||
bool eq_char(expr* ch1, expr* ch2);
|
||||
bool neq_char(expr* ch1, expr* ch2);
|
||||
bool le_char(expr* ch1, expr* ch2);
|
||||
bool pred_implies(expr* a, expr* b);
|
||||
bool are_complements(expr* r1, expr* r2) const;
|
||||
|
@ -286,6 +302,8 @@ class seq_rewriter {
|
|||
expr_ref zero() { return expr_ref(m_autil.mk_int(0), m()); }
|
||||
expr_ref one() { return expr_ref(m_autil.mk_int(1), m()); }
|
||||
expr_ref minus_one() { return expr_ref(m_autil.mk_int(-1), m()); }
|
||||
expr_ref mk_sub(expr* a, rational const& n);
|
||||
expr_ref mk_sub(expr* a, unsigned n) { return mk_sub(a, rational(n)); }
|
||||
|
||||
bool is_suffix(expr* s, expr* offset, expr* len);
|
||||
bool is_prefix(expr* s, expr* offset, expr* len);
|
||||
|
@ -379,9 +397,19 @@ public:
|
|||
|
||||
void add_seqs(expr_ref_vector const& ls, expr_ref_vector const& rs, expr_ref_pair_vector& new_eqs);
|
||||
|
||||
// Expose derivative and nullability check
|
||||
/*
|
||||
create the nullability check for r
|
||||
*/
|
||||
expr_ref is_nullable(expr* r);
|
||||
/*
|
||||
make the derivative of r wrt the given element ele
|
||||
*/
|
||||
expr_ref mk_derivative(expr* ele, expr* r);
|
||||
/*
|
||||
make the derivative of r wrt the canonical variable v0 = (:var 0),
|
||||
for example mk_derivative(a+) = (if (v0 = 'a') then a* else [])
|
||||
*/
|
||||
expr_ref mk_derivative(expr* r);
|
||||
|
||||
// heuristic elimination of element from condition that comes form a derivative.
|
||||
// special case optimization for conjunctions of equalities, disequalities and ranges.
|
||||
|
|
|
@ -28,8 +28,8 @@ skolem::skolem(ast_manager& m, th_rewriter& rw):
|
|||
m_tail = "seq.tail";
|
||||
m_seq_first = "seq.first";
|
||||
m_seq_last = "seq.last";
|
||||
m_indexof_left = "seq.idx.left";
|
||||
m_indexof_right = "seq.idx.right";
|
||||
m_indexof_left = "seq.idx.l";
|
||||
m_indexof_right = "seq.idx.r";
|
||||
m_aut_step = "aut.step";
|
||||
m_pre = "seq.pre"; // (seq.pre s l): prefix of string s of length l
|
||||
m_post = "seq.post"; // (seq.post s l): suffix of string s of length k, based on extract starting at index i of length l
|
||||
|
|
|
@ -77,8 +77,10 @@ namespace seq {
|
|||
|
||||
expr_ref mk_indexof_left(expr* t, expr* s, expr* offset = nullptr) { return mk(m_indexof_left, t, s, offset); }
|
||||
expr_ref mk_indexof_right(expr* t, expr* s, expr* offset = nullptr) { return mk(m_indexof_right, t, s, offset); }
|
||||
expr_ref mk_last_indexof_left(expr* t, expr* s, expr* offset = nullptr) { return mk("seq.last_indexof_left", t, s, offset); }
|
||||
expr_ref mk_last_indexof_right(expr* t, expr* s, expr* offset = nullptr) { return mk("seq.last_indexof_right", t, s, offset); }
|
||||
expr_ref mk_contains_left(expr* t, expr* s, expr* offset = nullptr) { return mk("seq.cnt.l", t, s, offset); }
|
||||
expr_ref mk_contains_right(expr* t, expr* s, expr* offset = nullptr) { return mk("seq.cnt.r", t, s, offset); }
|
||||
expr_ref mk_last_indexof_left(expr* t, expr* s, expr* offset = nullptr) { return mk("seq.lidx.l", t, s, offset); }
|
||||
expr_ref mk_last_indexof_right(expr* t, expr* s, expr* offset = nullptr) { return mk("seq.lidx.r", t, s, offset); }
|
||||
|
||||
expr_ref mk_tail(expr* s, expr* i) { return mk(m_tail, s, i); }
|
||||
expr_ref mk_post(expr* s, expr* i) { return mk(m_post, s, i); }
|
||||
|
|
|
@ -839,7 +839,7 @@ bool seq_util::str::is_nth_i(expr const* n, expr*& s, unsigned& idx) const {
|
|||
return arith_util(m).is_unsigned(i, idx);
|
||||
}
|
||||
|
||||
app* seq_util::str::mk_nth_i(expr* s, unsigned i) const {
|
||||
app* seq_util::str::mk_nth_c(expr* s, unsigned i) const {
|
||||
return mk_nth_i(s, arith_util(m).mk_int(i));
|
||||
}
|
||||
|
||||
|
@ -854,6 +854,48 @@ void seq_util::str::get_concat(expr* e, expr_ref_vector& es) const {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Returns true if s is an expression of the form (l = |u|) |u|-k or (-k)+|u| or |u|+(-k).
|
||||
Also returns true and assigns k=0 and l=s if s is |u|.
|
||||
*/
|
||||
bool seq_util::str::is_len_sub(expr const* s, expr*& l, expr*& u, rational& k) const {
|
||||
expr* x;
|
||||
rational v;
|
||||
arith_util a(m);
|
||||
if (is_length(s, l)) {
|
||||
k = 0;
|
||||
return true;
|
||||
}
|
||||
else if (a.is_sub(s, l, x) && is_length(l, u) && a.is_numeral(x, v) && v.is_nonneg()) {
|
||||
k = v;
|
||||
return true;
|
||||
}
|
||||
else if (a.is_add(s, l, x) && is_length(l, u) && a.is_numeral(x, v) && v.is_nonpos()) {
|
||||
k = - v;
|
||||
return true;
|
||||
}
|
||||
else if (a.is_add(s, x, l) && is_length(l, u) && a.is_numeral(x, v) && v.is_nonpos()) {
|
||||
k = - v;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool seq_util::str::is_unit_string(expr const* s, expr_ref& c) const {
|
||||
zstring z;
|
||||
expr* ch = nullptr;
|
||||
if (is_string(s, z) && z.length() == 1) {
|
||||
c = mk_char(z[0]);
|
||||
return true;
|
||||
}
|
||||
else if (is_unit(s, ch)) {
|
||||
c = ch;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void seq_util::str::get_concat_units(expr* e, expr_ref_vector& es) const {
|
||||
expr* e1, *e2;
|
||||
while (is_concat(e, e1, e2)) {
|
||||
|
@ -876,8 +918,6 @@ app* seq_util::str::mk_is_empty(expr* s) const {
|
|||
return m.mk_eq(s, mk_empty(s->get_sort()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
unsigned seq_util::str::min_length(expr* s) const {
|
||||
SASSERT(u.is_seq(s));
|
||||
unsigned result = 0;
|
||||
|
@ -892,7 +932,10 @@ unsigned seq_util::str::min_length(expr* s) const {
|
|||
return 0u;
|
||||
};
|
||||
while (is_concat(s, s1, s2)) {
|
||||
result += get_length(s1);
|
||||
if (is_concat(s1))
|
||||
result += min_length(s1);
|
||||
else
|
||||
result += get_length(s1);
|
||||
s = s2;
|
||||
}
|
||||
result += get_length(s);
|
||||
|
@ -920,7 +963,10 @@ unsigned seq_util::str::max_length(expr* s) const {
|
|||
return UINT_MAX;
|
||||
};
|
||||
while (is_concat(s, s1, s2)) {
|
||||
result = u.max_plus(get_length(s1), result);
|
||||
if (is_concat(s1))
|
||||
result = u.max_plus(max_length(s1), result);
|
||||
else
|
||||
result = u.max_plus(get_length(s1), result);
|
||||
s = s2;
|
||||
}
|
||||
result = u.max_plus(get_length(s), result);
|
||||
|
@ -1065,38 +1111,70 @@ app* seq_util::rex::mk_epsilon(sort* seq_sort) {
|
|||
/*
|
||||
Produces compact view of concrete concatenations such as (abcd).
|
||||
*/
|
||||
std::ostream& seq_util::rex::pp::compact_helper_seq(std::ostream& out, expr* s) const {
|
||||
SASSERT(re.u.is_seq(s));
|
||||
bool seq_util::rex::pp::print_seq(std::ostream& out, expr* s) const {
|
||||
zstring z;
|
||||
expr* x, * j, * k, * l, * i, * x_;
|
||||
if (re.u.str.is_empty(s))
|
||||
out << "()";
|
||||
else if (re.u.str.is_unit(s))
|
||||
seq_unit(out, s);
|
||||
else if (re.u.str.is_concat(s)) {
|
||||
expr_ref_vector es(re.m);
|
||||
re.u.str.get_concat(s, es);
|
||||
for (expr* e : es)
|
||||
compact_helper_seq(out, e);
|
||||
print(out, e);
|
||||
}
|
||||
else if (re.u.str.is_string(s, z)) {
|
||||
for (unsigned i = 0; i < z.length(); i++)
|
||||
out << (char)z[i];
|
||||
}
|
||||
//using braces to indicate 'full' output
|
||||
//for example an uninterpreted constant X will be printed as {X}
|
||||
//while a unit sequence "X" will be printed as X
|
||||
//thus for example (concat "X" "Y" Z "W") where Z is uninterpreted is printed as XY{Z}W
|
||||
else out << "{" << mk_pp(s, re.m) << "}";
|
||||
return out;
|
||||
else if (re.u.str.is_at(s, x, i))
|
||||
print(out, x) << "@", print(out, i);
|
||||
else if (re.u.str.is_extract(s, x, j, k)) {
|
||||
rational jv, iv;
|
||||
print(out, x);
|
||||
if (arith_util(re.m).is_numeral(j, jv)) {
|
||||
if (arith_util(re.m).is_numeral(k, iv)) {
|
||||
// output X[j,k]
|
||||
out << "[" << jv.get_int32() << "," << jv.get_int32() << "]";
|
||||
}
|
||||
else if (arith_util(re.m).is_sub(k, l, i) && re.u.str.is_length(l, x_) && x == x_ &&
|
||||
arith_util(re.m).is_numeral(i, iv) && iv == jv) {
|
||||
// case X[j,|X|-j] is denoted by X[j..]
|
||||
out << "[" << jv.get_int32() << "..]";
|
||||
}
|
||||
else if (((arith_util(re.m).is_add(k, l, i) && re.u.str.is_length(l, x_)) ||
|
||||
(arith_util(re.m).is_add(k, i, l) && re.u.str.is_length(l, x_))) && x == x_ &&
|
||||
arith_util(re.m).is_numeral(i, iv) && iv.get_int32() + jv.get_int32() == 0) {
|
||||
// case X[j,|X|-j] is denoted by X[j..]
|
||||
out << "[" << jv.get_int32() << "..]";
|
||||
}
|
||||
else {
|
||||
out << "[" << jv.get_int32() << ",";
|
||||
print(out, k);
|
||||
out << "]";
|
||||
}
|
||||
}
|
||||
else {
|
||||
out << "[";
|
||||
print(out, j);
|
||||
out << ",";
|
||||
print(out, k);
|
||||
out << "]";
|
||||
}
|
||||
}
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
Produces output such as [a-z] for a range.
|
||||
*/
|
||||
std::ostream& seq_util::rex::pp::compact_helper_range(std::ostream& out, expr* s1, expr* s2) const {
|
||||
std::ostream& seq_util::rex::pp::print_range(std::ostream& out, expr* s1, expr* s2) const {
|
||||
out << "[";
|
||||
seq_unit(out, s1) << "-";
|
||||
seq_unit(out, s2) << "]";
|
||||
print(out, s1);
|
||||
out << "-";
|
||||
print(out, s2);
|
||||
out << "]";
|
||||
return out;
|
||||
}
|
||||
|
||||
|
@ -1111,9 +1189,10 @@ bool seq_util::rex::pp::can_skip_parenth(expr* r) const {
|
|||
/*
|
||||
Specialize output for a unit sequence converting to visible ASCII characters if possible.
|
||||
*/
|
||||
std::ostream& seq_util::rex::pp::seq_unit(std::ostream& out, expr* s) const {
|
||||
expr* e;
|
||||
bool seq_util::rex::pp::print_unit(std::ostream& out, expr* s) const {
|
||||
expr* e, * i;
|
||||
unsigned n = 0;
|
||||
|
||||
if ((re.u.str.is_unit(s, e) && re.u.is_const_char(e, n)) || re.u.is_const_char(s, n)) {
|
||||
char c = (char)n;
|
||||
if (c == '\n')
|
||||
|
@ -1122,22 +1201,21 @@ std::ostream& seq_util::rex::pp::seq_unit(std::ostream& out, expr* s) const {
|
|||
out << "\\r";
|
||||
else if (c == '\f')
|
||||
out << "\\f";
|
||||
else if (c == ' ')
|
||||
out << "\\s";
|
||||
else if (c == '(' || c == ')' || c == '{' || c == '}' || c == '[' || c == ']' || c == '.' || c == '\\')
|
||||
out << "\\" << c;
|
||||
else if (32 < n && n < 127) {
|
||||
else if (32 <= n && n < 127 && n != '\"' && n != ' '
|
||||
&& n != '\\' && n != '\'' && n != '?' && n != '.' && n != '(' && n != ')' && n != '[' && n != ']'
|
||||
&& n != '{' && n != '}' && n != '&') {
|
||||
if (html_encode) {
|
||||
if (c == '<')
|
||||
out << "<";
|
||||
else if (c == '>')
|
||||
out << ">";
|
||||
else if (c == '&')
|
||||
out << "&";
|
||||
else if (c == '\"')
|
||||
out << """;
|
||||
//else if (c == '&')
|
||||
// out << "&";
|
||||
//else if (c == '\"')
|
||||
// out << """;
|
||||
else
|
||||
out << "\\x" << std::hex << n;
|
||||
//out << "\\x" << std::hex << n;
|
||||
out << c;
|
||||
}
|
||||
else
|
||||
out << c;
|
||||
|
@ -1148,95 +1226,193 @@ std::ostream& seq_util::rex::pp::seq_unit(std::ostream& out, expr* s) const {
|
|||
out << "\\x" << std::hex << n;
|
||||
else if (n <= 0xFFF)
|
||||
out << "\\u0" << std::hex << n;
|
||||
else
|
||||
else
|
||||
out << "\\u" << std::hex << n;
|
||||
}
|
||||
else if (re.u.str.is_nth_i(s, e, i)) {
|
||||
print(out, e) << "[";
|
||||
print(out, i) << "]";
|
||||
}
|
||||
else if (re.u.str.is_length(s, e))
|
||||
print(out << "|", e) << "|";
|
||||
else
|
||||
out << "{" << mk_pp(s, re.m) << "}";
|
||||
return out;
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
Pretty prints the regex r into the out stream
|
||||
Pretty prints the regex r into the ostream out
|
||||
*/
|
||||
std::ostream& seq_util::rex::pp::display(std::ostream& out) const {
|
||||
std::ostream& seq_util::rex::pp::print(std::ostream& out, expr* e) const {
|
||||
expr* r1 = nullptr, * r2 = nullptr, * s = nullptr, * s2 = nullptr;
|
||||
unsigned lo = 0, hi = 0;
|
||||
if (re.u.is_char(e))
|
||||
return seq_unit(out, e);
|
||||
else if (re.u.is_seq(e))
|
||||
return compact_helper_seq(out, e);
|
||||
arith_util a(re.m);
|
||||
rational v;
|
||||
if (!e)
|
||||
out << "null";
|
||||
else if (print_unit(out, e))
|
||||
;
|
||||
else if (print_seq(out, e))
|
||||
;
|
||||
else if (re.is_full_char(e))
|
||||
return out << ".";
|
||||
out << ".";
|
||||
else if (re.is_full_seq(e))
|
||||
return out << ".*";
|
||||
out << ".*";
|
||||
else if (re.is_to_re(e, s))
|
||||
return compact_helper_seq(out, s);
|
||||
else if (re.is_range(e, s, s2))
|
||||
return compact_helper_range(out, s, s2);
|
||||
print(out, s);
|
||||
else if (re.is_range(e, s, s2))
|
||||
print_range(out, s, s2);
|
||||
else if (re.is_epsilon(e))
|
||||
return out << "()";
|
||||
// ε = epsilon
|
||||
out << (html_encode ? "ε" : "()");
|
||||
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) << "&" /*(html_encode ? ")&(" : ")&(")*/ << pp(re, r2) << ")";
|
||||
// ∅ = emptyset
|
||||
out << (html_encode ? "∅" : "[]");
|
||||
else if (re.is_concat(e, r1, r2)) {
|
||||
print(out, r1);
|
||||
print(out, r2);
|
||||
}
|
||||
else if (re.is_antimorov_union(e, r1, r2) || re.is_union(e, r1, r2)) {
|
||||
out << "(";
|
||||
print(out, r1);
|
||||
out << (html_encode ? "⋃" : "|");
|
||||
print(out, r2);
|
||||
out << ")";
|
||||
}
|
||||
else if (re.is_intersection(e, r1, r2)) {
|
||||
out << "(";
|
||||
print(out, r1);
|
||||
out << (html_encode ? "⋂" : "&");
|
||||
print(out, r2);
|
||||
out << ")";
|
||||
}
|
||||
else if (re.is_complement(e, r1)) {
|
||||
out << "~";
|
||||
if (can_skip_parenth(r1))
|
||||
return out << "~" << pp(re, r1);
|
||||
else
|
||||
return out << "~(" << pp(re, r1) << ")";
|
||||
print(out, r1);
|
||||
else {
|
||||
out << "(";
|
||||
print(out, r1);
|
||||
out << ")";
|
||||
}
|
||||
}
|
||||
else if (re.is_plus(e, r1)) {
|
||||
if (can_skip_parenth(r1))
|
||||
return out << pp(re, r1) << "+";
|
||||
else
|
||||
return out << "(" << pp(re, r1) << ")+";
|
||||
if (can_skip_parenth(r1)) {
|
||||
print(out, r1);
|
||||
out << "+";
|
||||
}
|
||||
else {
|
||||
out << "(";
|
||||
print(out, r1);
|
||||
out << ")+";
|
||||
}
|
||||
}
|
||||
else if (re.is_star(e, r1)) {
|
||||
if (can_skip_parenth(r1))
|
||||
return out << pp(re, r1) << "*";
|
||||
else
|
||||
return out << "(" << pp(re, r1) << ")*";
|
||||
if (can_skip_parenth(r1)) {
|
||||
print(out, r1);
|
||||
out << "*";
|
||||
}
|
||||
else {
|
||||
out << "(";
|
||||
print(out, r1);
|
||||
out << ")*";
|
||||
}
|
||||
}
|
||||
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 << ",}";
|
||||
if (can_skip_parenth(r1))
|
||||
print(out, r1) << "{" << lo << ",}";
|
||||
else {
|
||||
out << "(";
|
||||
print(out, r1);
|
||||
out << "){" << lo << ",}";
|
||||
}
|
||||
}
|
||||
else if (re.is_loop(e, r1, lo, hi)) {
|
||||
if (can_skip_parenth(r1)) {
|
||||
print(out, r1);
|
||||
if (lo == hi)
|
||||
return out << pp(re, r1) << "{" << lo << "}";
|
||||
else
|
||||
return out << pp(re, r1) << "{" << lo << "," << hi << "}";
|
||||
out << "{" << lo << "}";
|
||||
else
|
||||
out << "{" << lo << "," << hi << "}";
|
||||
}
|
||||
else {
|
||||
out << "(";
|
||||
print(out, r1);
|
||||
if (lo == hi)
|
||||
return out << "(" << pp(re, r1) << "){" << lo << "}";
|
||||
out << "){" << lo << "}";
|
||||
else
|
||||
return out << "(" << pp(re, r1) << "){" << lo << "," << hi << "}";
|
||||
out << "){" << 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_diff(e, r1, r2)) {
|
||||
out << "(";
|
||||
print(out, r1);
|
||||
out << ")\\(";
|
||||
print(out, r2);
|
||||
out << ")";
|
||||
}
|
||||
else if (re.m.is_ite(e, s, r1, r2)) {
|
||||
out << (html_encode ? "(𝐢𝐟 " : "(if ");
|
||||
print(out, s);
|
||||
out << (html_encode ? " 𝐭𝗵𝐞𝐧 " : " then ");
|
||||
print(out, r1);
|
||||
out << (html_encode ? " 𝐞𝐥𝘀𝐞 " : " else ");
|
||||
print(out, r2);
|
||||
out << ")";
|
||||
}
|
||||
else if (re.is_opt(e, r1)) {
|
||||
if (can_skip_parenth(r1))
|
||||
return out << pp(re, r1) << "?";
|
||||
else
|
||||
return out << "(" << pp(re, r1) << ")?";
|
||||
print(out, r1) << "?";
|
||||
else {
|
||||
out << "(";
|
||||
print(out, r1);
|
||||
out << ")?";
|
||||
}
|
||||
}
|
||||
else if (re.is_reverse(e, r1)) {
|
||||
out << "(reverse ";
|
||||
print(out, r1);
|
||||
out << ")";
|
||||
}
|
||||
else if (re.m.is_eq(e, r1, r2)) {
|
||||
out << "(";
|
||||
print(out, r1);
|
||||
out << " = ";
|
||||
print(out, r2);
|
||||
out << ")";
|
||||
}
|
||||
else if (re.m.is_not(e, r1)) {
|
||||
out << "!";
|
||||
print(out, r1);
|
||||
}
|
||||
else if (a.is_add(e, s, s2) && a.is_numeral(s, v) && v < 0)
|
||||
print(out, s2) << " - " << -v;
|
||||
else if (a.is_add(e, s, s2) && a.is_numeral(s2, v) && v < 0)
|
||||
print(out, s) << " - " << -v;
|
||||
else if (a.is_add(e, s, s2))
|
||||
print(out, s) << " + ", print(out, s2);
|
||||
else if (a.is_sub(e, s, s2) && a.is_numeral(s2, v) && v > 0)
|
||||
print(out, s) << " - " << v;
|
||||
else if (a.is_le(e, s, s2))
|
||||
print(out << "(", s) << " <= ", print(out, s2) << ")";
|
||||
else if (re.m.is_value(e))
|
||||
out << mk_pp(e, re.m);
|
||||
else if (is_app(e) && to_app(e)->get_num_args() == 0)
|
||||
out << mk_pp(e, re.m);
|
||||
else if (is_app(e)) {
|
||||
out << "(" << to_app(e)->get_decl()->get_name();
|
||||
for (expr* arg : *to_app(e))
|
||||
print(out << " ", arg);
|
||||
out << ")";
|
||||
}
|
||||
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) << "}";
|
||||
// for all remaining cases use the default pretty printer
|
||||
out << mk_pp(e, re.m);
|
||||
return out;
|
||||
}
|
||||
|
||||
std::ostream& seq_util::rex::pp::display(std::ostream& out) const {
|
||||
return print(out, ex);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1244,7 +1420,16 @@ std::ostream& seq_util::rex::pp::display(std::ostream& out) const {
|
|||
*/
|
||||
std::string seq_util::rex::to_str(expr* r) const {
|
||||
std::ostringstream out;
|
||||
out << pp(u.re, r);
|
||||
pp(u.re, r, false).display(out);
|
||||
return out.str();
|
||||
}
|
||||
|
||||
/*
|
||||
Pretty prints the regex r into the output string that is htmlencoded
|
||||
*/
|
||||
std::string seq_util::rex::to_strh(expr* r) const {
|
||||
std::ostringstream out;
|
||||
pp(u.re, r, true).display(out);
|
||||
return out.str();
|
||||
}
|
||||
|
||||
|
@ -1290,7 +1475,7 @@ seq_util::rex::info seq_util::rex::get_info_rec(expr* e) const {
|
|||
else
|
||||
result = mk_info_rec(to_app(e));
|
||||
m_infos.setx(e->get_id(), result, invalid_info);
|
||||
STRACE("re_info", tout << "compute_info(" << pp(u.re, e) << ")=" << result << std::endl;);
|
||||
STRACE("re_info", tout << "compute_info(" << pp(u.re, e, false) << ")=" << result << std::endl;);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -1518,7 +1703,13 @@ seq_util::rex::info seq_util::rex::info::orelse(seq_util::rex::info const& i) co
|
|||
// unsigned ite_min_length = std::min(min_length, i.min_length);
|
||||
// lbool ite_nullable = (nullable == i.nullable ? nullable : l_undef);
|
||||
// TBD: whether ite is interpreted or not depends on whether the condition is interpreted and both branches are interpreted
|
||||
return info(false, false, false, false, normalized && i.normalized, monadic && i.monadic, singleton && i.singleton, nullable, std::min(min_length, i.min_length), std::max(star_height, i.star_height));
|
||||
return info(false, false, false, false,
|
||||
normalized && i.normalized,
|
||||
monadic && i.monadic,
|
||||
singleton && i.singleton,
|
||||
((nullable == l_true && i.nullable == l_true) ? l_true : ((nullable == l_false && i.nullable == l_false) ? l_false : l_undef)),
|
||||
std::min(min_length, i.min_length),
|
||||
std::max(star_height, i.star_height));
|
||||
}
|
||||
else
|
||||
return i;
|
||||
|
|
|
@ -286,7 +286,8 @@ public:
|
|||
app* mk_at(expr* s, expr* i) const { expr* es[2] = { s, i }; return m.mk_app(m_fid, OP_SEQ_AT, 2, es); }
|
||||
app* mk_nth(expr* s, expr* i) const { expr* es[2] = { s, i }; return m.mk_app(m_fid, OP_SEQ_NTH, 2, es); }
|
||||
app* mk_nth_i(expr* s, expr* i) const { expr* es[2] = { s, i }; return m.mk_app(m_fid, OP_SEQ_NTH_I, 2, es); }
|
||||
app* mk_nth_i(expr* s, unsigned i) const;
|
||||
app* mk_nth_u(expr* s, expr* i) const { expr* es[2] = { s, i }; return m.mk_app(m_fid, OP_SEQ_NTH_U, 2, es); }
|
||||
app* mk_nth_c(expr* s, unsigned i) const;
|
||||
|
||||
app* mk_substr(expr* a, expr* b, expr* c) const { expr* es[3] = { a, b, c }; return m.mk_app(m_fid, OP_SEQ_EXTRACT, 3, es); }
|
||||
app* mk_contains(expr* a, expr* b) const { expr* es[2] = { a, b }; return m.mk_app(m_fid, OP_SEQ_CONTAINS, 2, es); }
|
||||
|
@ -350,6 +351,13 @@ public:
|
|||
bool is_from_code(expr const* n) const { return is_app_of(n, m_fid, OP_STRING_FROM_CODE); }
|
||||
bool is_to_code(expr const* n) const { return is_app_of(n, m_fid, OP_STRING_TO_CODE); }
|
||||
|
||||
bool is_len_sub(expr const* n, expr*& l, expr*& u, rational& k) const;
|
||||
|
||||
/*
|
||||
tests if s is a single character string(c) or a unit (c)
|
||||
*/
|
||||
bool is_unit_string(expr const* s, expr_ref& c) const;
|
||||
|
||||
bool is_string_term(expr const * n) const {
|
||||
return u.is_string(n->get_sort());
|
||||
}
|
||||
|
@ -530,7 +538,20 @@ public:
|
|||
bool is_loop(expr const* n) const { return is_app_of(n, m_fid, OP_RE_LOOP); }
|
||||
bool is_empty(expr const* n) const { return is_app_of(n, m_fid, OP_RE_EMPTY_SET); }
|
||||
bool is_full_char(expr const* n) const { return is_app_of(n, m_fid, OP_RE_FULL_CHAR_SET); }
|
||||
bool is_full_seq(expr const* n) const { return is_app_of(n, m_fid, OP_RE_FULL_SEQ_SET); }
|
||||
bool is_full_seq(expr const* n) const {
|
||||
expr* s;
|
||||
return is_app_of(n, m_fid, OP_RE_FULL_SEQ_SET) || (is_star(n, s) && is_full_char(s));
|
||||
}
|
||||
bool is_dot_plus(expr const* n) const {
|
||||
expr* s, * t;
|
||||
if (is_plus(n, s) && is_full_char(s))
|
||||
return true;
|
||||
if (is_concat(n, s, t)) {
|
||||
if ((is_full_char(s) && is_full_seq(t)) || (is_full_char(t) && is_full_seq(s)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool is_of_pred(expr const* n) const { return is_app_of(n, m_fid, OP_RE_OF_PRED); }
|
||||
bool is_reverse(expr const* n) const { return is_app_of(n, m_fid, OP_RE_REVERSE); }
|
||||
bool is_derivative(expr const* n) const { return is_app_of(n, m_fid, OP_RE_DERIVATIVE); }
|
||||
|
@ -559,18 +580,32 @@ public:
|
|||
app* mk_epsilon(sort* seq_sort);
|
||||
info get_info(expr* r) const;
|
||||
std::string to_str(expr* r) const;
|
||||
std::string to_strh(expr* r) const;
|
||||
|
||||
expr_ref mk_ite_simplify(expr* c, expr* t, expr* e)
|
||||
{
|
||||
expr_ref result(m);
|
||||
if (m.is_true(c) || t == e)
|
||||
result = t;
|
||||
else if (m.is_false(c))
|
||||
result = e;
|
||||
else
|
||||
result = m.mk_ite(c, t, e);
|
||||
return result;
|
||||
}
|
||||
|
||||
class pp {
|
||||
seq_util::rex& re;
|
||||
expr* e;
|
||||
expr* ex;
|
||||
bool html_encode;
|
||||
bool can_skip_parenth(expr* r) const;
|
||||
std::ostream& seq_unit(std::ostream& out, expr* s) const;
|
||||
std::ostream& compact_helper_seq(std::ostream& out, expr* s) const;
|
||||
std::ostream& compact_helper_range(std::ostream& out, expr* s1, expr* s2) const;
|
||||
bool print_unit(std::ostream& out, expr* s) const;
|
||||
bool print_seq(std::ostream& out, expr* s) const;
|
||||
std::ostream& print_range(std::ostream& out, expr* s1, expr* s2) const;
|
||||
std::ostream& print(std::ostream& out, expr* e) const;
|
||||
|
||||
public:
|
||||
pp(seq_util::rex& r, expr* e, bool html = false) : re(r), e(e), html_encode(html) {}
|
||||
pp(seq_util::rex& re, expr* ex, bool html) : re(re), ex(ex), html_encode(html) {}
|
||||
std::ostream& display(std::ostream&) const;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -87,6 +87,7 @@ public:
|
|||
bool is_special_relation(app* e) const { return is_special_relation(e->get_decl()); }
|
||||
sr_property get_property(func_decl* f) const;
|
||||
sr_property get_property(app* e) const { return get_property(e->get_decl()); }
|
||||
func_decl* get_relation(func_decl* f) const { SASSERT(is_special_relation(f)); return to_func_decl(f->get_parameter(0).get_ast()); }
|
||||
|
||||
func_decl* mk_to_decl(func_decl* f) { return mk_rel_decl(f, OP_SPECIAL_RELATION_TO); }
|
||||
func_decl* mk_po_decl(func_decl* f) { return mk_rel_decl(f, OP_SPECIAL_RELATION_PO); }
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue