Bi-directional unrolling of R* regular expressions (#3532)
authorAndres Noetzli <andres.noetzli@gmail.com>
Thu, 5 Dec 2019 14:39:32 +0000 (06:39 -0800)
committerAndrew Reynolds <andrew.j.reynolds@gmail.com>
Thu, 5 Dec 2019 14:39:32 +0000 (08:39 -0600)
src/theory/strings/regexp_operation.cpp
test/regress/CMakeLists.txt
test/regress/regress0/strings/bidir_star.smt2 [new file with mode: 0644]

index aaf1c864f29ec23d67a5380916cad16038fc2128..8707593c733e3e9b6a41dbb5e3e621b3bce46b22 100644 (file)
@@ -1168,18 +1168,36 @@ void RegExpOpr::simplifyPRegExp( Node s, Node r, std::vector< Node > &new_nodes
         } else if(r[0].getKind() == kind::REGEXP_SIGMA) {
           conc = d_true;
         } else {
+          NodeManager* nm = NodeManager::currentNM();
           Node se = s.eqNode(d_emptyString);
-          Node sinr = NodeManager::currentNM()->mkNode(kind::STRING_IN_REGEXP, s, r[0]);
-          Node sk1 = NodeManager::currentNM()->mkSkolem( "rs", s.getType(), "created for regular expression star" );
-          Node sk2 = NodeManager::currentNM()->mkSkolem( "rs", s.getType(), "created for regular expression star" );
-          Node s1nz = sk1.eqNode(d_emptyString).negate();
-          Node s2nz = sk2.eqNode(d_emptyString).negate();
-          Node s1inr = NodeManager::currentNM()->mkNode(kind::STRING_IN_REGEXP, sk1, r[0]);
-          Node s2inrs = NodeManager::currentNM()->mkNode(kind::STRING_IN_REGEXP, sk2, r);
-          Node s12 = s.eqNode(NodeManager::currentNM()->mkNode(kind::STRING_CONCAT, sk1, sk2));
+          Node sinr = nm->mkNode(kind::STRING_IN_REGEXP, s, r[0]);
+          Node sk1 = nm->mkSkolem(
+              "rs", s.getType(), "created for regular expression star");
+          Node sk2 = nm->mkSkolem(
+              "rs", s.getType(), "created for regular expression star");
+          Node sk3 = nm->mkSkolem(
+              "rs", s.getType(), "created for regular expression star");
 
-          conc = NodeManager::currentNM()->mkNode(kind::AND, s12, s1nz, s2nz, s1inr, s2inrs);
-          conc = NodeManager::currentNM()->mkNode(kind::OR, se, sinr, conc);
+          NodeBuilder<> nb(kind::AND);
+          nb << sk1.eqNode(d_emptyString).negate();
+          nb << sk3.eqNode(d_emptyString).negate();
+          nb << nm->mkNode(kind::STRING_IN_REGEXP, sk1, r[0]);
+          nb << nm->mkNode(kind::STRING_IN_REGEXP, sk2, r);
+          nb << nm->mkNode(kind::STRING_IN_REGEXP, sk3, r[0]);
+          nb << s.eqNode(nm->mkNode(kind::STRING_CONCAT, sk1, sk2, sk3));
+          conc = nb;
+
+          // We unfold `x in R*` by considering three cases: `x` is empty, `x`
+          // is matched by `R`, or `x` is matched by two or more `R`s. For the
+          // last case, we break `x` into three pieces, making the beginning
+          // and the end each match `R` and the middle match `R*`. Matching the
+          // beginning and the end with `R` allows us to reason about the
+          // beginning and the end of `x` simultaneously.
+          //
+          // x in R* ---> (x = "") v (x in R) v
+          //              (x = x1 ++ x2 ++ x3 ^ x1 != "" ^ x3 != "" ^
+          //               x1 in R ^ x2 in R* ^ x3 in R)
+          conc = nm->mkNode(kind::OR, se, sinr, conc);
         }
         break;
       }
index 911943c64c85379cddb77a95ea14f3e45cbba067..90038872f5541b883c1c556cbb1114412b4e156a 100644 (file)
@@ -858,6 +858,7 @@ set(regress_0_tests
   regress0/smtlib/reset-force-logic.smt2
   regress0/smtlib/reset-set-logic.smt2
   regress0/smtlib/set-info-status.smt2
+  regress0/strings/bidir_star.smt2
   regress0/strings/bug001.smt2
   regress0/strings/bug002.smt2
   regress0/strings/bug612.smt2
diff --git a/test/regress/regress0/strings/bidir_star.smt2 b/test/regress/regress0/strings/bidir_star.smt2
new file mode 100644 (file)
index 0000000..7303d13
--- /dev/null
@@ -0,0 +1,8 @@
+; COMMAND-LINE: --strings-exp
+(set-logic SLIA)
+(declare-fun a () String)
+(assert (>= (str.len a) 2))
+(assert (str.in.re a (re.+ (re.range "0" "1"))))
+(assert (<= 3 (str.to.int (str.substr a (+ (- 2) (str.len a)) 1))))
+(set-info :status unsat)
+(check-sat)