3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-05-31 06:07:46 +00:00

updated tests

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2026-04-02 10:03:29 -07:00
parent 34cb0a17fc
commit b0a4a15c98
3 changed files with 106 additions and 86 deletions

View file

@ -20,15 +20,15 @@
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <setjmp.h> #include <setjmp.h>
#ifdef _WIN32
#include <windows.h>
#include <crtdbg.h>
#include <signal.h> #include <signal.h>
static jmp_buf jmp_env; static jmp_buf jmp_env;
static volatile int in_test = 0; static volatile int in_test = 0;
#ifdef _WIN32
#include <windows.h>
#include <crtdbg.h>
void abort_handler(int sig) { void abort_handler(int sig) {
(void)sig; (void)sig;
if (in_test) { if (in_test) {
@ -50,13 +50,6 @@ void suppress_dialogs() {
_set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
signal(SIGABRT, abort_handler); signal(SIGABRT, abort_handler);
} }
#else
void suppress_dialogs() {}
#endif
static int tests_run = 0;
static int tests_passed = 0;
static int tests_crashed = 0;
#define RUN_TEST(name) do { \ #define RUN_TEST(name) do { \
fprintf(stderr, "[TEST] Running %s\n", #name); \ fprintf(stderr, "[TEST] Running %s\n", #name); \
@ -79,6 +72,40 @@ static int tests_crashed = 0;
} \ } \
} while(0) } while(0)
#else
void abort_handler(int sig) {
(void)sig;
if (in_test) {
in_test = 0;
signal(SIGABRT, abort_handler);
longjmp(jmp_env, 1);
}
}
void suppress_dialogs() { signal(SIGABRT, abort_handler); }
#define RUN_TEST(name) do { \
fprintf(stderr, "[TEST] Running %s\n", #name); \
tests_run++; \
in_test = 1; \
if (setjmp(jmp_env) == 0) { \
name(); \
in_test = 0; \
tests_passed++; \
fprintf(stderr, "[TEST] PASS %s\n", #name); \
} else { \
tests_crashed++; \
fprintf(stderr, "[TEST] ABORT %s (caught SIGABRT)\n", #name); \
} \
} while(0)
#endif
static int tests_run = 0;
static int tests_passed = 0;
static int tests_crashed = 0;
/* ===== Helpers ===== */ /* ===== Helpers ===== */
static Z3_sort mk_string_sort(Z3_context ctx) { return Z3_mk_string_sort(ctx); } static Z3_sort mk_string_sort(Z3_context ctx) { return Z3_mk_string_sort(ctx); }
static Z3_ast mk_str(Z3_context ctx, const char* s) { return Z3_mk_string(ctx, s); } static Z3_ast mk_str(Z3_context ctx, const char* s) { return Z3_mk_string(ctx, s); }

View file

@ -4,15 +4,15 @@
#include <stdlib.h> #include <stdlib.h>
#include <setjmp.h> #include <setjmp.h>
#ifdef _WIN32
#include <windows.h>
#include <crtdbg.h>
#include <signal.h> #include <signal.h>
static jmp_buf jmp_env; static jmp_buf jmp_env;
static volatile int in_test = 0; static volatile int in_test = 0;
#ifdef _WIN32
#include <windows.h>
#include <crtdbg.h>
void abort_handler(int sig) { void abort_handler(int sig) {
(void)sig; (void)sig;
if (in_test) { if (in_test) {
@ -34,12 +34,6 @@ void suppress_dialogs() {
_set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
signal(SIGABRT, abort_handler); signal(SIGABRT, abort_handler);
} }
#else
void suppress_dialogs() {}
#endif
static int tests_run = 0;
static int tests_passed = 0;
#define RUN_TEST(name) do { \ #define RUN_TEST(name) do { \
fprintf(stderr, "[TEST] Running %s\n", #name); \ fprintf(stderr, "[TEST] Running %s\n", #name); \
@ -60,6 +54,38 @@ static int tests_passed = 0;
} \ } \
} while(0) } while(0)
#else
void abort_handler(int sig) {
(void)sig;
if (in_test) {
in_test = 0;
signal(SIGABRT, abort_handler);
longjmp(jmp_env, 1);
}
}
void suppress_dialogs() { signal(SIGABRT, abort_handler); }
#define RUN_TEST(name) do { \
fprintf(stderr, "[TEST] Running %s\n", #name); \
tests_run++; \
in_test = 1; \
if (setjmp(jmp_env) == 0) { \
name(); \
in_test = 0; \
tests_passed++; \
fprintf(stderr, "[TEST] PASS %s\n", #name); \
} else { \
fprintf(stderr, "[TEST] ABORT %s (caught SIGABRT)\n", #name); \
} \
} while(0)
#endif
static int tests_run = 0;
static int tests_passed = 0;
/* Helper to create string sort, variables, constants */ /* Helper to create string sort, variables, constants */
Z3_sort mk_string_sort(Z3_context ctx) { return Z3_mk_string_sort(ctx); } Z3_sort mk_string_sort(Z3_context ctx) { return Z3_mk_string_sort(ctx); }
Z3_ast mk_string_var(Z3_context ctx, const char* name) { Z3_ast mk_string_var(Z3_context ctx, const char* name) {

View file

@ -1125,80 +1125,41 @@ namespace seq {
} }
} }
// consume symbolic characters (s_unit tokens) via uniform derivative // consume symbolic characters via uniform derivatives
// check. When all minterms of the regex produce the same Brzozowski
// derivative, the derivative is independent of the character value
// and we can deterministically consume the token. Forward direction
// only (matches ZIPT history tracking convention).
//
// Extended: when uniform derivative fails but the token has a char_range
// constraint (from apply_regex_var_split), check if the char_range is a
// subset of a single minterm's character class. If so, the derivative
// is deterministic for that token.
// Mirrors ZIPT StrMem.SimplifyCharRegex lines 96-117.
for (str_mem& mem : m_str_mem) { for (str_mem& mem : m_str_mem) {
SASSERT(mem.m_str && mem.m_regex); SASSERT(mem.m_str && mem.m_regex);
if (mem.is_primitive()) if (mem.is_primitive())
continue; continue;
while (mem.m_str && !mem.m_str->is_empty()) { while (mem.m_str && !mem.m_str->is_empty()) {
euf::snode* tok = mem.m_str->first();
if (!tok || !tok->is_unit()) // TODO: generalize this to work for reverse derivative as well.
euf::snode *tok = mem.m_str->first();
if (!tok || !tok->is_char_or_unit())
break; break;
// seq_rewriter rw(m);
// TODO -rewrite to use symbolic derivative and add back resulting derived regex. expr_ref d(rw.mk_derivative(mem.m_regex->get_expr()), m);
//
// compute minterms and check for uniform derivative // Extract the inner char expression from seq.unit(?inner)
euf::snode_vector minterms; expr *unit_expr = tok->arg(0)->get_expr(), *inner_char;
sg.compute_minterms(mem.m_regex, minterms);
VERIFY(!minterms.empty()); // substitute the inner char for the derivative variable in d
// try char_range subset approach. var_subst vs(m);
// If the symbolic char has a char_range constraint and that d = vs(d, inner_char);
// range is a subset of exactly one minterm's character class,
// we can deterministically take that minterm's derivative. th_rewriter thrw(m);
SASSERT(m_graph.m_parikh); thrw(d);
auto full = char_set::full(zstring::max_char());
char_set* cs; auto next = sg.mk(d);
dep_tracker dep; if (next->is_fail()) {
if (m_char_ranges.contains(tok->id())) { m_is_general_conflict = true;
auto& ranges = m_char_ranges[tok->id()]; set_conflict(backtrack_reason::regex, mem.m_dep);
cs = &ranges.first; return simplify_result::conflict;
dep = ranges.second;
}
else {
cs = &full;
dep = graph().dep_mgr().mk_empty();
} }
if (!cs->is_empty()) { mem.m_str = sg.drop_left(mem.m_str, 1);
euf::snode* matching_deriv = nullptr; mem.m_regex = next;
bool found = false; mem.m_history = sg.mk_concat(mem.m_history, tok);
for (euf::snode* mt : minterms) {
SASSERT(mt && mt->get_expr());
SASSERT(!mt->is_fail());
char_set mt_cs = m_graph.m_seq_regex->minterm_to_char_set(mt->get_expr());
if (cs->is_subset(mt_cs)) {
euf::snode* deriv = sg.brzozowski_deriv(mem.m_regex, mt);
if (!deriv) { found = false; break; }
if (deriv->is_fail()) {
m_is_general_conflict = true;
set_conflict(backtrack_reason::regex, graph().dep_mgr().mk_join(mem.m_dep, dep));
return simplify_result::conflict;
}
matching_deriv = deriv;
found = true;
break;
}
}
if (found && matching_deriv) {
mem.m_str = sg.drop_left(mem.m_str, 1);
mem.m_regex = matching_deriv;
mem.m_history = sg.mk_concat(mem.m_history, tok);
continue;
}
}
break;
} }
} }
@ -3140,12 +3101,13 @@ namespace seq {
} }
return false; return false;
} }
bool nielsen_graph::apply_regex_unit_split(nielsen_node *node) { bool nielsen_graph::apply_regex_unit_split(nielsen_node *node) {
for (str_mem const &mem : node->str_mems()) { for (str_mem const &mem : node->str_mems()) {
SASSERT(mem.m_str && mem.m_regex); SASSERT(mem.m_str && mem.m_regex);
if (mem.is_primitive()) if (mem.is_primitive())
continue; continue;
euf::snode *first = mem.m_str->first(); euf::snode *first = mem.m_str->first();
if (!first || !first->is_char_or_unit()) if (!first || !first->is_char_or_unit())
continue; continue;
@ -3272,6 +3234,11 @@ namespace seq {
created = true; created = true;
} }
// TODO: replace this with a version that just uses symbolic derivatives
// and not min-terms.
// It solves x -> unit(nth(x, 0)) ++ x
// Then the split_regex_unit can solve process the node further.
// Branch 2+: for each minterm m_i, x → ?c · x // Branch 2+: for each minterm m_i, x → ?c · x
// where ?c is a symbolic char constrained by the minterm // where ?c is a symbolic char constrained by the minterm
for (euf::snode* mt : minterms) { for (euf::snode* mt : minterms) {