3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-05-15 22:55:33 +00:00

Added classical regex factorization

This commit is contained in:
CEisenhofer 2026-04-02 20:03:22 +02:00
parent 3ca960d679
commit a81ce477f5
8 changed files with 258 additions and 6 deletions

View file

@ -154,6 +154,7 @@ namespace euf {
n->m_ground = l->is_ground() && r->is_ground();
n->m_regex_free = l->is_regex_free() && r->is_regex_free();
n->m_nullable = l->is_nullable() && r->is_nullable();
n->m_is_classical = l->is_classical() && r->is_classical();
n->m_level = std::max(l->level(), r->level()) + 1;
n->m_length = l->length() + r->length();
++m_stats.m_num_concat;
@ -163,13 +164,14 @@ namespace euf {
case snode_kind::s_power: {
// s^n: nullable follows base, consistent with ZIPT's PowerToken
// the exponent n is assumed to be a symbolic integer, may or may not be zero
// NSB review: SASSERT(n->num_args() == 2); and simplify code
// NSB review: SASSERT(n->num_args() == 2); and simplify code
// NSB review: is this the correct definition of ground what about the exponent?
SASSERT(n->num_args() >= 1);
snode* base = n->arg(0);
n->m_ground = base->is_ground();
n->m_regex_free = base->is_regex_free();
n->m_nullable = base->is_nullable();
n->m_is_classical = base->is_classical();
n->m_level = 1;
n->m_length = 1;
++m_stats.m_num_power;
@ -181,6 +183,7 @@ namespace euf {
n->m_ground = n->arg(0)->is_ground();
n->m_regex_free = false;
n->m_nullable = true;
n->m_is_classical = n->arg(0)->is_classical();
n->m_level = 1;
n->m_length = 1;
break;
@ -189,6 +192,7 @@ namespace euf {
n->m_ground = n->num_args() > 0 ? n->arg(0)->is_ground() : true;
n->m_regex_free = false;
// nullable iff lower bound is 0: r{0,n} accepts the empty string
n->m_is_classical = n->arg(0)->is_classical();
// default lo=1 (non-nullable) in case extraction fails
unsigned lo = 1, hi = 1;
expr* loop_body = nullptr;
@ -207,15 +211,17 @@ namespace euf {
n->m_ground = n->arg(0)->is_ground() && n->arg(1)->is_ground();
n->m_regex_free = false;
n->m_nullable = n->arg(0)->is_nullable() || n->arg(1)->is_nullable();
n->m_is_classical = n->arg(0)->is_classical() && n->arg(1)->is_classical();
n->m_level = 1;
n->m_length = 1;
break;
case snode_kind::s_intersect:
SASSERT(n->num_args() == 2);
n->m_ground = n->arg(0)->is_ground() && n->arg(1)->is_ground();
n->m_ground = n->arg(0)->is_ground() && n->arg(1)->is_ground();
n->m_regex_free = false;
n->m_nullable = n->arg(0)->is_nullable() && n->arg(1)->is_nullable();
n->m_is_classical = false;
n->m_level = 1;
n->m_length = 1;
break;
@ -225,6 +231,7 @@ namespace euf {
n->m_ground = n->arg(0)->is_ground();
n->m_regex_free = false;
n->m_nullable = !n->arg(0)->is_nullable();
n->m_is_classical = false;
n->m_level = 1;
n->m_length = 1;
break;
@ -233,6 +240,7 @@ namespace euf {
n->m_ground = true;
n->m_regex_free = false;
n->m_nullable = false;
n->m_is_classical = false;
n->m_level = 1;
n->m_length = 1;
break;

View file

@ -63,10 +63,11 @@ namespace euf {
unsigned m_num_args = 0;
// metadata flags, analogous to ZIPT's Str/StrToken properties
bool m_ground = true; // no uninterpreted string variables
bool m_regex_free = true; // no regex constructs
bool m_nullable = false; // accepts the empty string
unsigned m_level = 0; // tree depth/level (0 for empty, 1 for singletons)
bool m_ground = true; // no uninterpreted string variables
bool m_regex_free = true; // no regex constructs
bool m_nullable = false; // accepts the empty string
bool m_is_classical = true; // classical regular expression
unsigned m_level = 0; // tree depth/level (0 for empty, 1 for singletons)
unsigned m_length = 0; // token count, number of leaf tokens in the tree
// hash matrix for associativity-respecting hashing (2x2 polynomial hash matrix)
@ -104,6 +105,7 @@ namespace euf {
bool is_ground() const { return m_ground; }
bool is_regex_free() const { return m_regex_free; }
bool is_nullable() const { return m_nullable; }
bool is_classical() const { return m_is_classical; }
unsigned level() const { return m_level; }
unsigned length() const { return m_length; }