mirror of
https://github.com/Z3Prover/z3
synced 2026-06-23 00:50:29 +00:00
Smart constructors for regex ranges: canonical form at construction time (#9814)
Regex range expressions (`re.range`) and Boolean operations over them were left in unsimplified form, defeating downstream optimisations (bisimulation classical fast-path, derivative engine) and producing semantically-empty terms not syntactically equal to `re.none`. ## Changes ### `seq_decl_plugin.h` / `seq_decl_plugin.cpp` - **`seq_util::rex::mk_range(sort*, unsigned lo, unsigned hi)`** — new smart constructor that normalises at call time: - `lo > hi` → `re.empty` - `lo == hi` → `str.to_re` (singleton string) - `lo < hi` → `re.range` - **`mk_info_rec` `OP_RE_RANGE`** — concrete non-empty ranges (both bounds are single-char literals with `lo ≤ hi`) now return `classical = true`, enabling the XOR-bisimulation `classical_distinguishing` fast-path on character-predicate leaves. Symbolic/unknown ranges retain `classical = false`. ### `seq_rewriter.cpp` - **`mk_re_range`** — singleton collapse: `(re.range "a" "a")` → `(str.to_re "a")` - **`mk_regex_inter_normalize`** — range × range intersection: `[a,b] ∩ [c,d]` → `[max(a,c), min(b,d)]`, or `re.none` (disjoint), or `str.to_re` (boundary singleton); now delegates to `re().mk_range(sort*, lo, hi)` - **`mk_regex_union_normalize`** — range × range union for overlapping/adjacent ranges: `[a,b] ∪ [c,d]` → `[min(a,c), max(b,d)]`; disjoint ranges fall through to existing `merge_regex_sets`; now delegates to `re().mk_range(sort*, lo, hi)` - **`mk_re_complement`** — range complement expands to one or two concrete ranges instead of an opaque `re.comp` node; now delegates to `re().mk_range(sort*, lo, hi)`: - `comp([0, b])` → `[b+1, max]` - `comp([a, max])` → `[0, a-1]` - `comp([a, b])` → `[0, a-1] ∪ [b+1, max]` ``` (simplify (re.range "z" "a")) ; → re.none (simplify (re.range "a" "a")) ; → (str.to_re "a") (simplify (re.inter (re.range "a" "z") (re.range "f" "k"))); → (re.range "f" "k") (simplify (re.union (re.range "a" "f") (re.range "g" "k"))); → (re.range "a" "k") (simplify (re.comp (re.range "b" "y"))) ; → (re.union [0,a] [z,max]) ``` ### Tests New `src/test/seq_rewriter.cpp` with 14 cases covering all the above reductions plus downstream propagation (star/concat/union/inter absorbing empty ranges). --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
parent
897c4475af
commit
8c2a425e4b
6 changed files with 272 additions and 4 deletions
|
|
@ -521,6 +521,8 @@ public:
|
|||
app* mk_to_re(expr* s) { return m.mk_app(m_fid, OP_SEQ_TO_RE, 1, &s); }
|
||||
app* mk_in_re(expr* s, expr* r) { return m.mk_app(m_fid, OP_SEQ_IN_RE, s, r); }
|
||||
app* mk_range(expr* s1, expr* s2) { return m.mk_app(m_fid, OP_RE_RANGE, s1, s2); }
|
||||
// Smart constructor: returns re.empty / str.to_re / re.range based on lo vs hi.
|
||||
app* mk_range(sort* re_sort, unsigned lo, unsigned hi);
|
||||
app* mk_concat(expr* r1, expr* r2) { return m.mk_app(m_fid, OP_RE_CONCAT, r1, r2); }
|
||||
app* mk_union(expr* r1, expr* r2) { return m.mk_app(m_fid, OP_RE_UNION, r1, r2); }
|
||||
app* mk_inter(expr* r1, expr* r2) { return m.mk_app(m_fid, OP_RE_INTERSECT, r1, r2); }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue