├── runalltests ├── project └── assembly.sbt ├── lib └── automaton.jar ├── papers └── popl18-efficient.pdf ├── AUTHORS ├── tests ├── simple-replace-1e.smt2 ├── simple-replace.smt2 ├── simple-replace-1d.smt2 ├── simple-replace-1f.smt2 ├── simple-replace-1g.smt2 ├── simple-replace-1b.smt2 ├── simple-replace-1c.smt2 ├── escapeSequences-1a.smt2 ├── escapeSequences-1b.smt2 ├── nonlinear-2.smt2 ├── nonlinear.smt2 ├── simple-cycle.smt2 ├── simple-replace-5.smt2 ├── simple-replace-2.smt2 ├── simple-replace-2d.smt2 ├── empty-concat.smt2 ├── simple-cycle2.smt2 ├── simple-replace-2c.smt2 ├── norn-benchmark-9k.smt2 ├── norn-benchmark-9g.smt2 ├── simple-replace-3b.smt2 ├── simple-replace-3.smt2 ├── epsilon-2.smt2 ├── simple-replace-4c.smt2 ├── simple-replace-2b.smt2 ├── simple-replace-4.smt2 ├── simple-replace-4b.smt2 ├── norn-benchmark-9.smt2 ├── norn-benchmark-9c.smt2 ├── norn-benchmark-9e.smt2 ├── norn-benchmark-9b.smt2 ├── norn-benchmark-9h.smt2 ├── norn-benchmark-9f.smt2 ├── simple-cvc-smtlib.smt2 ├── norn-benchmark-9d.smt2 ├── norn-benchmark-9j.smt2 ├── norn-benchmark-9i.smt2 ├── transducer2b.smt2 ├── transducer2c.smt2 ├── transducer2d.smt2 ├── transducer2.smt2 ├── membership_427.smt2 ├── cvc_replace_28.smt2 ├── simple-concat.smt2 ├── simple-concat-2.smt2 ├── simple-concat-4.smt2 ├── simple-concat-4b.smt2 ├── simple-concat-5.smt2 ├── simple-concat-5b.smt2 ├── simple-concat-3.smt2 ├── epsilon-3.smt2 ├── epsilon-1.smt2 ├── transducer1.smt2 ├── extract-1.smt2 ├── extract-1b.smt2 ├── runtests ├── cvc_replace_185.smt2 ├── concat2.smt2 ├── concat.smt2 ├── cvc_replace_4062.smt2 ├── transducer3.smt2 └── Answers ├── sloth ├── rundir ├── README.md ├── src └── main │ └── scala │ ├── Flags.scala │ ├── RRFunsToAFA.scala │ ├── StringTheoryUtil.scala │ ├── SMTLIBStringTheory.scala │ ├── ServerMain.scala │ ├── StringPreprocessing.scala │ ├── SimpleModelChecker.scala │ ├── Regex2AFA.scala │ ├── SMTLIBMain.scala │ ├── SMTReader.scala │ ├── Replace.scala │ ├── ReplaceAll.scala │ ├── Emptiness.scala │ ├── AFormula.scala │ ├── AFA.scala │ └── StringTheoryTranslator.scala └── sloth-client /runalltests: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ./rundir tests "" +assert +model 4 | -------------------------------------------------------------------------------- /project/assembly.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.5") 2 | -------------------------------------------------------------------------------- /lib/automaton.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uuverifiers/sloth/HEAD/lib/automaton.jar -------------------------------------------------------------------------------- /papers/popl18-efficient.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uuverifiers/sloth/HEAD/papers/popl18-efficient.pdf -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Petr Janku 2 | http://www.fit.vutbr.cz/~ijanku/ 3 | 4 | Philipp Ruemmer 5 | http://www.philipp.ruemmer.org 6 | 7 | -------------------------------------------------------------------------------- /tests/simple-replace-1e.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | (declare-fun x_7 () String) 4 | (declare-fun x_10 () String) 5 | 6 | (assert (= x_7 (str.replace "HelloWorld" "e" "a"))) 7 | 8 | (check-sat) 9 | (get-model) 10 | -------------------------------------------------------------------------------- /tests/simple-replace.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | (declare-fun x_7 () String) 4 | (declare-fun x_10 () String) 5 | 6 | (assert (= x_7 (str.replaceall "HelloWorld" "e" "a"))) 7 | 8 | (check-sat) 9 | (get-model) 10 | -------------------------------------------------------------------------------- /tests/simple-replace-1d.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | (declare-fun x_7 () String) 4 | (declare-fun x_10 () String) 5 | 6 | (assert (= x_7 (str.replace "HelloWorld" "e" ""))) 7 | (assert (= x_7 "hlloWorld")) 8 | 9 | (check-sat) 10 | (get-model) -------------------------------------------------------------------------------- /tests/simple-replace-1f.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | (declare-fun x_7 () String) 4 | (declare-fun x_10 () String) 5 | 6 | (assert (= x_7 (str.replace "HelloWorld" "e" "a"))) 7 | (assert (= x_7 "HolloWorld")) 8 | 9 | (check-sat) 10 | (get-model) 11 | -------------------------------------------------------------------------------- /tests/simple-replace-1g.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | (declare-fun x_7 () String) 4 | (declare-fun x_10 () String) 5 | 6 | (assert (= x_7 (str.replace "HelloWorld" "e" "a"))) 7 | (assert (= x_7 "HalloWorld")) 8 | 9 | (check-sat) 10 | (get-model) 11 | -------------------------------------------------------------------------------- /tests/simple-replace-1b.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | (declare-fun x_7 () String) 4 | (declare-fun x_10 () String) 5 | 6 | (assert (= x_7 (str.replaceall "HelloWorld" "e" "a"))) 7 | (assert (= x_7 "HolloWorld")) 8 | 9 | (check-sat) 10 | (get-model) 11 | -------------------------------------------------------------------------------- /tests/simple-replace-1c.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | (declare-fun x_7 () String) 4 | (declare-fun x_10 () String) 5 | 6 | (assert (= x_7 (str.replaceall "HelloWorld" "e" "a"))) 7 | (assert (= x_7 "HalloWorld")) 8 | 9 | (check-sat) 10 | (get-model) 11 | -------------------------------------------------------------------------------- /tests/escapeSequences-1a.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | 4 | (declare-fun x () String) 5 | (declare-fun y () String) 6 | 7 | (assert (= x "\x48\145\x6cl\o")) 8 | 9 | ;unsat 10 | (assert (str.in.re x (re.* (re.range "a" "u")))) 11 | (check-sat) 12 | 13 | (check-sat) 14 | -------------------------------------------------------------------------------- /tests/escapeSequences-1b.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | 4 | (declare-fun x () String) 5 | (declare-fun y () String) 6 | 7 | (assert (= x "\x68\145\x6cl\o")) 8 | 9 | ;sat 10 | (assert (str.in.re x (re.* (re.range "a" "u")))) 11 | (check-sat) 12 | 13 | (check-sat) 14 | -------------------------------------------------------------------------------- /tests/nonlinear-2.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | (declare-fun a () String) 4 | (declare-fun b () String) 5 | 6 | (assert (= a (str.++ b b))) 7 | (assert (str.in.re a (re.++ (re.* (str.to.re "(")) 8 | (re.+ (str.to.re ")"))))) 9 | 10 | (check-sat) 11 | 12 | -------------------------------------------------------------------------------- /tests/nonlinear.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | (declare-fun a () String) 4 | (declare-fun b () String) 5 | 6 | (assert (= a (str.++ b b))) 7 | (assert (str.in.re a (re.++ (re.+ (str.to.re "(")) 8 | (re.+ (str.to.re ")"))))) 9 | 10 | (check-sat) 11 | 12 | -------------------------------------------------------------------------------- /tests/simple-cycle.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | (declare-fun a () String) 4 | (declare-fun b () String) 5 | (declare-fun c () String) 6 | (declare-fun d () String) 7 | 8 | (assert (= a (str.++ b c))) 9 | (assert (= b (str.++ d a))) 10 | 11 | (assert (not (= c ""))) 12 | 13 | (check-sat) -------------------------------------------------------------------------------- /tests/simple-replace-5.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | (declare-fun x_7 () String) 4 | (declare-fun x_10 () String) 5 | 6 | (assert (= x_10 "This is a very long sentence with code")) 7 | (assert (= x_7 (str.replaceall x_10 "" ""))) 8 | 9 | (check-sat) 10 | (get-model) 11 | 12 | -------------------------------------------------------------------------------- /tests/simple-replace-2.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | (declare-const a String) 4 | (declare-const b String) 5 | (declare-fun x_7 () String) 6 | (declare-fun x_10 () String) 7 | 8 | (assert (= x_10 (str.++ a b))) 9 | (assert (= x_7 (str.replaceall x_10 "e" "a"))) 10 | 11 | (check-sat) 12 | (get-model) 13 | -------------------------------------------------------------------------------- /tests/simple-replace-2d.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | (declare-const a String) 4 | (declare-const b String) 5 | (declare-fun x_7 () String) 6 | (declare-fun x_10 () String) 7 | 8 | (assert (= x_10 (str.++ a b))) 9 | (assert (= x_7 (str.replace x_10 "e" "a"))) 10 | 11 | (check-sat) 12 | (get-model) 13 | -------------------------------------------------------------------------------- /tests/empty-concat.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | (declare-fun sigmaStar_0 () String) 4 | (declare-fun literal_1 () String) 5 | (declare-fun x_2 () String) 6 | (declare-fun literal_3 () String) 7 | (declare-fun x_4 () String) 8 | 9 | (assert (= x_2 (str.++ literal_1 sigmaStar_0))) 10 | (check-sat) 11 | (get-model) 12 | -------------------------------------------------------------------------------- /tests/simple-cycle2.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | (declare-fun a () String) 4 | (declare-fun b () String) 5 | (declare-fun c () String) 6 | (declare-fun d () String) 7 | (declare-fun e () String) 8 | 9 | (assert (= a (str.++ b (str.++ c e)))) 10 | (assert (= b (str.++ d a))) 11 | 12 | (assert (not (= e ""))) 13 | 14 | (check-sat) -------------------------------------------------------------------------------- /tests/simple-replace-2c.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | (declare-const a String) 4 | (declare-const b String) 5 | (declare-fun x_7 () String) 6 | (declare-fun x_10 () String) 7 | 8 | (assert (= x_10 (str.++ a b))) 9 | (assert (= x_7 (str.replaceall x_10 "e" "a"))) 10 | (assert (str.in.re x_10 (str.to.re "Hello"))) 11 | (assert (str.in.re x_7 (str.to.re "Hallo"))) 12 | 13 | (check-sat) 14 | (get-model) 15 | -------------------------------------------------------------------------------- /tests/norn-benchmark-9k.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | (declare-fun var_0 () String) 4 | (declare-fun var_1 () String) 5 | (declare-fun var_2 () String) 6 | (declare-fun var_3 () String) 7 | (declare-fun var_4 () String) 8 | (declare-fun var_5 () String) 9 | (declare-fun var_6 () String) 10 | (declare-fun var_7 () String) 11 | (declare-fun var_8 () String) 12 | 13 | (assert (= "abc" "abd")) 14 | (check-sat) 15 | -------------------------------------------------------------------------------- /tests/norn-benchmark-9g.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | (declare-fun var_0 () String) 4 | (declare-fun var_1 () String) 5 | (declare-fun var_2 () String) 6 | (declare-fun var_3 () String) 7 | (declare-fun var_4 () String) 8 | (declare-fun var_5 () String) 9 | (declare-fun var_6 () String) 10 | (declare-fun var_7 () String) 11 | (declare-fun var_8 () String) 12 | 13 | (assert (not (= "abc" "abd"))) 14 | (check-sat) 15 | -------------------------------------------------------------------------------- /tests/simple-replace-3b.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | (declare-const a String) 4 | (declare-const b String) 5 | (declare-fun x_7 () String) 6 | (declare-fun x_10 () String) 7 | 8 | (assert (= x_7 (str.replace x_10 "e" "X"))) 9 | (assert (str.in.re x_7 (re.++ re-full-set 10 | (str.to.re "e") 11 | re-full-set))) 12 | 13 | (check-sat) 14 | (get-model) 15 | 16 | -------------------------------------------------------------------------------- /tests/simple-replace-3.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | (declare-const a String) 4 | (declare-const b String) 5 | (declare-fun x_7 () String) 6 | (declare-fun x_10 () String) 7 | 8 | (assert (= x_7 (str.replaceall x_10 "e" "X"))) 9 | (assert (str.in.re x_7 (re.++ re-full-set 10 | (str.to.re "e") 11 | re-full-set))) 12 | 13 | (check-sat) 14 | (get-model) 15 | 16 | -------------------------------------------------------------------------------- /tests/epsilon-2.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | (set-option :strings-exp true) 3 | (set-option :produce-models true) 4 | (declare-fun sigmaStar_2 () String) 5 | (declare-fun epsilon () String) 6 | (declare-fun x_3 () String) 7 | (assert (= epsilon "")) 8 | (assert (= x_3 (str.replace epsilon "http://" "https://"))) 9 | (assert (str.in.re x_3 (re.++ (re.* re.allchar) (re.++ (str.to.re "/evil") (re.* re.allchar))))) 10 | (check-sat) 11 | (get-model) 12 | -------------------------------------------------------------------------------- /tests/simple-replace-4c.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | (set-option :strings-exp true) 3 | (set-option :produce-models true) 4 | 5 | (declare-fun x_7 () String) 6 | (declare-fun x_10 () String) 7 | (declare-fun x_11 () String) 8 | (declare-fun x_12 () String) 9 | (declare-fun x_13 () String) 10 | 11 | 12 | (assert (= x_10 (str.++ x_11 x_12 ))) 13 | (assert (= x_7 (str.replace x_10 "e" "a"))) 14 | (assert (= x_7 "")) 15 | 16 | (check-sat) 17 | (get-model) 18 | -------------------------------------------------------------------------------- /tests/simple-replace-2b.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | (declare-const a String) 4 | (declare-const b String) 5 | (declare-fun x_7 () String) 6 | (declare-fun x_10 () String) 7 | 8 | (assert (= x_10 (str.++ a b))) 9 | (assert (or (= x_7 (str.replace x_10 "e" "a")) 10 | (= x_7 (str.replaceall x_10 "e" "a")))) 11 | (assert (str.in.re x_10 (str.to.re "Hello"))) 12 | (assert (str.in.re x_7 (str.to.re "Hollo"))) 13 | 14 | (check-sat) 15 | (get-model) 16 | -------------------------------------------------------------------------------- /tests/simple-replace-4.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | (set-option :strings-exp true) 3 | (set-option :produce-models true) 4 | 5 | (declare-fun x_7 () String) 6 | (declare-fun x_10 () String) 7 | (declare-fun x_11 () String) 8 | (declare-fun x_12 () String) 9 | (declare-fun x_13 () String) 10 | 11 | 12 | (assert (= x_10 (str.++ x_11 x_12 ))) 13 | (assert (= x_7 (str.replace x_10 "e" "a"))) 14 | (assert (= x_7 "hallo")) 15 | 16 | (check-sat) 17 | (get-model) 18 | -------------------------------------------------------------------------------- /tests/simple-replace-4b.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | (set-option :strings-exp true) 3 | (set-option :produce-models true) 4 | 5 | (declare-fun x_7 () String) 6 | (declare-fun x_10 () String) 7 | (declare-fun x_11 () String) 8 | (declare-fun x_12 () String) 9 | (declare-fun x_13 () String) 10 | 11 | 12 | (assert (= x_10 (str.++ x_11 x_12 ))) 13 | (assert (= x_7 (str.replace x_10 "e" "a"))) 14 | (assert (= x_7 "hello")) 15 | 16 | (check-sat) 17 | (get-model) 18 | -------------------------------------------------------------------------------- /sloth: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ $(uname) = "Linux" ]; then 4 | pathCmd="readlink -f" 5 | elif [ $(uname) = "Darwin" ]; then 6 | pathCmd="stat -f %N" 7 | else 8 | pathCmd="realpath" 9 | fi 10 | 11 | BASEDIR=`dirname $($pathCmd $0)` 12 | TARGET=`echo $BASEDIR/target/scala-*/sloth-assembly*.jar` 13 | LIBDIR=$BASEDIR/lib 14 | SCALA=scala 15 | JAVA=java 16 | export JAVA_OPTS="-Xss20000k -Xmx1500m" 17 | 18 | exec $JAVA -cp $TARGET strsolver.SMTLIBMain "$@" 19 | -------------------------------------------------------------------------------- /tests/norn-benchmark-9.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | (declare-fun var_0 () String) 4 | (declare-fun var_1 () String) 5 | (declare-fun var_2 () String) 6 | (declare-fun var_3 () String) 7 | (declare-fun var_4 () String) 8 | (declare-fun var_5 () String) 9 | (declare-fun var_6 () String) 10 | (declare-fun var_7 () String) 11 | (declare-fun var_8 () String) 12 | 13 | (assert (str.in.re "" (re.* (re.range "a" "u")))) 14 | (assert (not (str.in.re "" (re.* (str.to.re "b"))))) 15 | (check-sat) 16 | -------------------------------------------------------------------------------- /tests/norn-benchmark-9c.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | (declare-fun var_0 () String) 4 | (declare-fun var_1 () String) 5 | (declare-fun var_2 () String) 6 | (declare-fun var_3 () String) 7 | (declare-fun var_4 () String) 8 | (declare-fun var_5 () String) 9 | (declare-fun var_6 () String) 10 | (declare-fun var_7 () String) 11 | (declare-fun var_8 () String) 12 | 13 | (assert (str.in.re var_0 (re.* (re.range "a" "u")))) 14 | (assert (str.in.re var_0 (re.* (str.to.re "v")))) 15 | (check-sat) 16 | -------------------------------------------------------------------------------- /tests/norn-benchmark-9e.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | (declare-fun var_0 () String) 4 | (declare-fun var_1 () String) 5 | (declare-fun var_2 () String) 6 | (declare-fun var_3 () String) 7 | (declare-fun var_4 () String) 8 | (declare-fun var_5 () String) 9 | (declare-fun var_6 () String) 10 | (declare-fun var_7 () String) 11 | (declare-fun var_8 () String) 12 | 13 | (assert (str.in.re var_0 (re.* (re.range "m" "u")))) 14 | (assert (str.in.re var_0 (re.+ (str.to.re "v")))) 15 | (check-sat) 16 | -------------------------------------------------------------------------------- /tests/norn-benchmark-9b.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | (declare-fun var_0 () String) 4 | (declare-fun var_1 () String) 5 | (declare-fun var_2 () String) 6 | (declare-fun var_3 () String) 7 | (declare-fun var_4 () String) 8 | (declare-fun var_5 () String) 9 | (declare-fun var_6 () String) 10 | (declare-fun var_7 () String) 11 | (declare-fun var_8 () String) 12 | 13 | (assert (str.in.re var_0 (re.* (re.range "a" "u")))) 14 | (assert (not (str.in.re var_0 (re.* (str.to.re "b"))))) 15 | (check-sat) 16 | -------------------------------------------------------------------------------- /tests/norn-benchmark-9h.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | (declare-fun var_0 () String) 4 | (declare-fun var_1 () String) 5 | (declare-fun var_2 () String) 6 | (declare-fun var_3 () String) 7 | (declare-fun var_4 () String) 8 | (declare-fun var_5 () String) 9 | (declare-fun var_6 () String) 10 | (declare-fun var_7 () String) 11 | (declare-fun var_8 () String) 12 | 13 | (assert (str.in.re var_0 (str.to.re "abc"))) 14 | (assert (str.in.re var_1 (str.to.re "abc"))) 15 | (assert (not (= var_0 var_1))) 16 | (check-sat) 17 | -------------------------------------------------------------------------------- /tests/norn-benchmark-9f.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | (declare-fun var_0 () String) 4 | (declare-fun var_1 () String) 5 | (declare-fun var_2 () String) 6 | (declare-fun var_3 () String) 7 | (declare-fun var_4 () String) 8 | (declare-fun var_5 () String) 9 | (declare-fun var_6 () String) 10 | (declare-fun var_7 () String) 11 | (declare-fun var_8 () String) 12 | 13 | (assert (str.in.re var_0 (re.* (re.range "a" "u")))) 14 | (assert (str.in.re var_0 (re.* (str.to.re "v")))) 15 | (assert (not (= var_0 ""))) 16 | (check-sat) 17 | -------------------------------------------------------------------------------- /tests/simple-cvc-smtlib.smt2: -------------------------------------------------------------------------------- 1 | ; This example is currently beyond the scope of our tool, since 2 | ; it contains length constraints and integer variables 3 | 4 | (set-logic QF_S) 5 | 6 | (declare-fun a () String) 7 | (declare-fun b () String) 8 | (declare-fun a_len () Int) 9 | 10 | (assert (= b (str.++ a "c"))) 11 | ; (assert (= (str.len a) (str.len b))) 12 | (assert (str.in.re a (re.* (str.to.re "xy")))) 13 | (assert (not (str.in.re b (re.* (str.to.re "xy"))))) 14 | (assert (= (str.len a) a_len)) 15 | (assert (> a_len 0)) 16 | 17 | (check-sat) -------------------------------------------------------------------------------- /tests/norn-benchmark-9d.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | (declare-fun var_0 () String) 4 | (declare-fun var_1 () String) 5 | (declare-fun var_2 () String) 6 | (declare-fun var_3 () String) 7 | (declare-fun var_4 () String) 8 | (declare-fun var_5 () String) 9 | (declare-fun var_6 () String) 10 | (declare-fun var_7 () String) 11 | (declare-fun var_8 () String) 12 | 13 | ;(assert (str.in.re var_0 (re.* (re.range "a" "u")))) 14 | (assert (str.in.re var_0 (re.* (str.to.re "u")))) 15 | (assert (str.in.re var_0 (re.+ (str.to.re "v")))) 16 | (check-sat) 17 | -------------------------------------------------------------------------------- /rundir: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DIR="$1" 4 | shift 5 | 6 | ANSWER_SUFFIX="$1" 7 | shift 8 | 9 | [ -d "$DIR" ] || exit 1 10 | 11 | echo -n "$DIR ($@) ... " 12 | 13 | export TIMEFORMAT="%U" 14 | TIME=`time ( cd $DIR; ./runtests "$@" >Output"$ANSWER_SUFFIX" 2>&1; ) 2>&1` 15 | 16 | if cmp -s "$DIR"/Answers"$ANSWER_SUFFIX" "$DIR"/Output"$ANSWER_SUFFIX"; then 17 | echo -ne "\tSucceeded" 18 | else 19 | echo -ne "\tFAILED" 20 | cp "$DIR"/Output"$ANSWER_SUFFIX" "$DIR"/Output"$ANSWER_SUFFIX"-"`date`" 21 | fi 22 | 23 | echo -n " ($TIME" 24 | echo "s)" 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sloth 2 | ## An SMT Solver for String Constraints 3 | 4 | Sloth is a decision procedure for several relevant 5 | fragments of string constraints, including the straight-line fragment, 6 | which is sufficiently expressive for many applications from verification or 7 | security. Sloth uses succinct alternating finite-state automata 8 | (AFAs) as concise symbolic representations of string constraints, and 9 | uses model checking algorithms like IC3 for solving emptiness of 10 | the AFA. 11 | 12 | For documentation, see https://github.com/uuverifiers/sloth/wiki 13 | -------------------------------------------------------------------------------- /tests/norn-benchmark-9j.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | (declare-fun var_0 () String) 4 | (declare-fun var_1 () String) 5 | (declare-fun var_2 () String) 6 | (declare-fun var_3 () String) 7 | (declare-fun var_4 () String) 8 | (declare-fun var_5 () String) 9 | (declare-fun var_6 () String) 10 | (declare-fun var_7 () String) 11 | (declare-fun var_8 () String) 12 | 13 | (assert (str.in.re var_0 (str.to.re "abc"))) 14 | (assert (or (str.in.re var_1 (str.to.re "abd")) 15 | (str.in.re var_1 (str.to.re "ab")) 16 | (str.in.re var_1 (str.to.re "abcd")))) 17 | (assert (= var_0 var_1)) 18 | (check-sat) 19 | -------------------------------------------------------------------------------- /tests/norn-benchmark-9i.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | (declare-fun var_0 () String) 4 | (declare-fun var_1 () String) 5 | (declare-fun var_2 () String) 6 | (declare-fun var_3 () String) 7 | (declare-fun var_4 () String) 8 | (declare-fun var_5 () String) 9 | (declare-fun var_6 () String) 10 | (declare-fun var_7 () String) 11 | (declare-fun var_8 () String) 12 | 13 | (assert (str.in.re var_0 (str.to.re "abc"))) 14 | (assert (or (str.in.re var_1 (str.to.re "abd")) 15 | (str.in.re var_1 (str.to.re "ab")) 16 | (str.in.re var_1 (str.to.re "abcd")))) 17 | (assert (not (= var_0 var_1))) 18 | (check-sat) 19 | -------------------------------------------------------------------------------- /tests/transducer2b.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | ; to-uppercase transducer 4 | 5 | (define-fun-rec toUpper ((x String) (y String)) Bool 6 | (or (and (= x "") (= y "")) 7 | (and (= (seq-head y) 8 | (ite (and (bvule (_ bv97 8) (seq-head x)) ; 'a' 9 | (bvule (seq-head x) (_ bv122 8))) ; 'z' 10 | (bvsub (seq-head x) (_ bv32 8)) ; 'a' -> 'A', etc. 11 | (seq-head x))) 12 | (toUpper (seq-tail x) (seq-tail y)))) 13 | ) 14 | 15 | (declare-fun x () String) 16 | (declare-fun y () String) 17 | 18 | (assert (= x "Hello World")) 19 | (assert (toUpper x y)) 20 | (assert (= y "HELLO WORLT")) 21 | 22 | (check-sat) 23 | -------------------------------------------------------------------------------- /tests/transducer2c.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | ; to-uppercase transducer 4 | 5 | (define-fun-rec toUpper ((x String) (y String)) Bool 6 | (or (and (= x "") (= y "")) 7 | (and (= (seq-head y) 8 | (ite (and (bvule (_ bv97 8) (seq-head x)) ; 'a' 9 | (bvule (seq-head x) (_ bv122 8))) ; 'z' 10 | (bvsub (seq-head x) (_ bv32 8)) ; 'a' -> 'A', etc. 11 | (seq-head x))) 12 | (toUpper (seq-tail x) (seq-tail y)))) 13 | ) 14 | 15 | (declare-fun x () String) 16 | (declare-fun y () String) 17 | 18 | (assert (= x "Hello World")) 19 | (assert (toUpper x y)) 20 | (assert (= y "HELLO WORL")) 21 | 22 | (check-sat) 23 | -------------------------------------------------------------------------------- /tests/transducer2d.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | ; to-uppercase transducer 4 | 5 | (define-fun-rec toUpper ((x String) (y String)) Bool 6 | (or (and (= x "") (= y "")) 7 | (and (= (seq-head y) 8 | (ite (and (bvule (_ bv97 8) (seq-head x)) ; 'a' 9 | (bvule (seq-head x) (_ bv122 8))) ; 'z' 10 | (bvsub (seq-head x) (_ bv32 8)) ; 'a' -> 'A', etc. 11 | (seq-head x))) 12 | (toUpper (seq-tail x) (seq-tail y)))) 13 | ) 14 | 15 | (declare-fun x () String) 16 | (declare-fun y () String) 17 | 18 | (assert (= x "Hello World")) 19 | (assert (toUpper x y)) 20 | (assert (= y "HELLO WORLD")) 21 | 22 | (check-sat) 23 | -------------------------------------------------------------------------------- /tests/transducer2.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | ; to-uppercase transducer 4 | 5 | (define-fun-rec toUpper ((x String) (y String)) Bool 6 | (or (and (= x "") (= y "")) 7 | (and (= (seq-head y) 8 | (ite (and (bvule (_ bv97 8) (seq-head x)) ; 'a' 9 | (bvule (seq-head x) (_ bv122 8))) ; 'z' 10 | (bvsub (seq-head x) (_ bv32 8)) ; 'a' -> 'A', etc. 11 | (seq-head x))) 12 | (toUpper (seq-tail x) (seq-tail y)))) 13 | ) 14 | 15 | (declare-fun x () String) 16 | (declare-fun y () String) 17 | 18 | (assert (= x "Hello World")) 19 | (assert (toUpper x y)) 20 | (assert (not (= y "HELLO WORLD"))) 21 | 22 | (check-sat) 23 | -------------------------------------------------------------------------------- /tests/membership_427.smt2: -------------------------------------------------------------------------------- 1 | ;--- 2 | ; using 7-bit bit-vectors as characters 3 | ; check membership of .Net regex 4 | ; regexA = /([^\x00-\xFF]\s*)/u 5 | ;--- 6 | (set-info :status unsat) 7 | (set-option :print-success true) 8 | (set-logic QF_BVS) 9 | (define-sort CHAR () (_ BitVec 7)) 10 | (declare-const regexA (RegEx CHAR)) 11 | (declare-const x (Seq CHAR)) 12 | 13 | (assert (= regexA (re-concat (re-range #b0101111 #b0101111)(re-concat (re-concat (as re-empty-set (RegEx CHAR)) (re-star (re-union (re-range #b0001001 #b0001101) (re-range #b0100000 #b0100000)))) (re-of-seq (seq-cons #b0101111 (seq-cons #b1110101 (as seq-empty (Seq CHAR))))))))) 14 | 15 | ;check that the regex contains some x 16 | (assert (re-member x regexA)) 17 | (check-sat) 18 | -------------------------------------------------------------------------------- /tests/cvc_replace_28.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | (set-option :strings-exp true) 3 | (set-option :produce-models true) 4 | (declare-fun sigmaStar_0 () String) 5 | (declare-fun sigmaStar_6 () String) 6 | (declare-fun literal_9 () String) 7 | (declare-fun x_7 () String) 8 | (declare-fun x_10 () String) 9 | (declare-fun literal_11 () String) 10 | (declare-fun A () String) 11 | (assert (= x_7 (str.replace sigmaStar_0 "\x2f\x5b\x5e\x41\x53\x43\x52\x20\x2e\x5c\x2d\x40\x3a\x2f\x5d\x2f" "\x48\x45\x4c\x4c\x4f"))) 12 | (assert (= literal_9 "\x3c\x74\x64\x3e\x55\x52\x4c\x3a\x20")) 13 | (assert (= x_10 (str.++ literal_9 x_7))) 14 | (assert (= literal_11 "\x3c\x2f\x74\x64\x3e")) 15 | (assert (= A (str.++ x_10 literal_11))) 16 | (assert (str.in.re A (re.++ (re.* re.allchar) (re.++ (str.to.re "\x5c\x3c\x53\x43\x52\x49\x50\x54") (re.* re.allchar))))) 17 | 18 | (check-sat) 19 | (get-model) 20 | -------------------------------------------------------------------------------- /tests/simple-concat.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | ; to-uppercase transducer 4 | 5 | (define-fun-rec toUpper ((x String) (y String)) Bool 6 | (or (and (= x "") (= y "")) 7 | (and (= (seq-head y) 8 | (ite (and (bvule (_ bv97 8) (seq-head x)) ; 'a' 9 | (bvule (seq-head x) (_ bv122 8))) ; 'z' 10 | (bvsub (seq-head x) (_ bv32 8)) ; 'a' -> 'A', etc. 11 | (seq-head x))) 12 | (toUpper (seq-tail x) (seq-tail y)))) 13 | ) 14 | 15 | (declare-fun x0 () String) 16 | (declare-fun x1 () String) 17 | (declare-fun x2 () String) 18 | (declare-fun x3 () String) 19 | (declare-fun x4 () String) 20 | (declare-fun s0 () String) ; source variable 21 | 22 | (assert (= x2 (str.++ x0 x1))) 23 | (assert (toUpper x2 x3)) 24 | (assert (str.in.re x3 (re.+ (re.range "A" "Z")))) 25 | 26 | (check-sat) 27 | -------------------------------------------------------------------------------- /tests/simple-concat-2.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | ; to-uppercase transducer 4 | 5 | (define-fun-rec toUpper ((x String) (y String)) Bool 6 | (or (and (= x "") (= y "")) 7 | (and (= (seq-head y) 8 | (ite (and (bvule (_ bv97 8) (seq-head x)) ; 'a' 9 | (bvule (seq-head x) (_ bv122 8))) ; 'z' 10 | (bvsub (seq-head x) (_ bv32 8)) ; 'a' -> 'A', etc. 11 | (seq-head x))) 12 | (toUpper (seq-tail x) (seq-tail y)))) 13 | ) 14 | 15 | (declare-fun x0 () String) 16 | (declare-fun x1 () String) 17 | (declare-fun x2 () String) 18 | (declare-fun x3 () String) 19 | (declare-fun x4 () String) 20 | (declare-fun s0 () String) ; source variable 21 | 22 | ; should be unsat 23 | (assert (= x2 (str.++ x0 x1))) 24 | (assert (toUpper x2 x3)) 25 | (assert (str.in.re x3 (re.+ (re.range "a" "z")))) 26 | 27 | (check-sat) 28 | -------------------------------------------------------------------------------- /tests/simple-concat-4.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | ; to-uppercase transducer 4 | 5 | (define-fun-rec toUpper ((x String) (y String)) Bool 6 | (or (and (= x "") (= y "")) 7 | (and (= (seq-head y) 8 | (ite (and (bvule (_ bv97 8) (seq-head x)) ; 'a' 9 | (bvule (seq-head x) (_ bv122 8))) ; 'z' 10 | (bvsub (seq-head x) (_ bv32 8)) ; 'a' -> 'A', etc. 11 | (seq-head x))) 12 | (toUpper (seq-tail x) (seq-tail y)))) 13 | ) 14 | 15 | (declare-fun x0 () String) 16 | (declare-fun x1 () String) 17 | (declare-fun x2 () String) 18 | (declare-fun x3 () String) 19 | (declare-fun x4 () String) 20 | (declare-fun s0 () String) ; source variable 21 | 22 | (assert (str.in.re x1 (re.+ (re.range "a" "b")))) 23 | (assert (= x3 (str.++ x1 x2))) 24 | (assert (str.in.re x3 (re.+ (re.range "a" "d")))) 25 | 26 | (check-sat) 27 | -------------------------------------------------------------------------------- /tests/simple-concat-4b.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | ; to-uppercase transducer 4 | 5 | (define-fun-rec toUpper ((x String) (y String)) Bool 6 | (or (and (= x "") (= y "")) 7 | (and (= (seq-head y) 8 | (ite (and (bvule (_ bv97 8) (seq-head x)) ; 'a' 9 | (bvule (seq-head x) (_ bv122 8))) ; 'z' 10 | (bvsub (seq-head x) (_ bv32 8)) ; 'a' -> 'A', etc. 11 | (seq-head x))) 12 | (toUpper (seq-tail x) (seq-tail y)))) 13 | ) 14 | 15 | (declare-fun x0 () String) 16 | (declare-fun x1 () String) 17 | (declare-fun x2 () String) 18 | (declare-fun x3 () String) 19 | (declare-fun x4 () String) 20 | (declare-fun s0 () String) ; source variable 21 | 22 | (assert (str.in.re x1 (re.+ (re.range "a" "b")))) 23 | (assert (= x3 (str.++ x1 x2))) 24 | (assert (str.in.re x3 (re.+ (re.range "c" "d")))) 25 | 26 | (check-sat) 27 | -------------------------------------------------------------------------------- /tests/simple-concat-5.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | ; to-uppercase transducer 4 | 5 | (define-fun-rec toUpper ((x String) (y String)) Bool 6 | (or (and (= x "") (= y "")) 7 | (and (= (seq-head y) 8 | (ite (and (bvule (_ bv97 8) (seq-head x)) ; 'a' 9 | (bvule (seq-head x) (_ bv122 8))) ; 'z' 10 | (bvsub (seq-head x) (_ bv32 8)) ; 'a' -> 'A', etc. 11 | (seq-head x))) 12 | (toUpper (seq-tail x) (seq-tail y)))) 13 | ) 14 | 15 | (declare-fun x0 () String) 16 | (declare-fun x1 () String) 17 | (declare-fun x2 () String) 18 | (declare-fun x3 () String) 19 | (declare-fun x4 () String) 20 | (declare-fun s0 () String) ; source variable 21 | 22 | ; should be unsat 23 | (assert (= x2 (str.++ x0 "Hello World"))) 24 | (assert (toUpper x2 x3)) 25 | (assert (str.in.re x3 (re.+ (re.range "A" "Z")))) 26 | 27 | (check-sat) 28 | -------------------------------------------------------------------------------- /tests/simple-concat-5b.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | ; to-uppercase transducer 4 | 5 | (define-fun-rec toUpper ((x String) (y String)) Bool 6 | (or (and (= x "") (= y "")) 7 | (and (= (seq-head y) 8 | (ite (and (bvule (_ bv97 8) (seq-head x)) ; 'a' 9 | (bvule (seq-head x) (_ bv122 8))) ; 'z' 10 | (bvsub (seq-head x) (_ bv32 8)) ; 'a' -> 'A', etc. 11 | (seq-head x))) 12 | (toUpper (seq-tail x) (seq-tail y)))) 13 | ) 14 | 15 | (declare-fun x0 () String) 16 | (declare-fun x1 () String) 17 | (declare-fun x2 () String) 18 | (declare-fun x3 () String) 19 | (declare-fun x4 () String) 20 | (declare-fun s0 () String) ; source variable 21 | 22 | ; should be sat 23 | (assert (= x2 (str.++ x0 "HelloWorld"))) 24 | (assert (toUpper x2 x3)) 25 | (assert (str.in.re x3 (re.+ (re.range "A" "Z")))) 26 | 27 | (check-sat) 28 | -------------------------------------------------------------------------------- /tests/simple-concat-3.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | ; to-uppercase transducer 4 | 5 | (define-fun-rec toUpper ((x String) (y String)) Bool 6 | (or (and (= x "") (= y "")) 7 | (and (= (seq-head y) 8 | (ite (and (bvule (_ bv97 8) (seq-head x)) ; 'a' 9 | (bvule (seq-head x) (_ bv122 8))) ; 'z' 10 | (bvsub (seq-head x) (_ bv32 8)) ; 'a' -> 'A', etc. 11 | (seq-head x))) 12 | (toUpper (seq-tail x) (seq-tail y)))) 13 | ) 14 | 15 | (declare-fun x0 () String) 16 | (declare-fun x1 () String) 17 | (declare-fun x2 () String) 18 | (declare-fun x3 () String) 19 | (declare-fun x4 () String) 20 | (declare-fun s0 () String) ; source variable 21 | 22 | (assert (str.in.re x0 (re.+ (re.range "a" "z")))) 23 | (assert (toUpper x0 x1)) 24 | (assert (= x3 (str.++ x1 x2))) 25 | (assert (str.in.re x3 (re.+ (re.range "a" "z")))) 26 | 27 | (check-sat) 28 | -------------------------------------------------------------------------------- /tests/epsilon-3.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | (set-option :strings-exp true) 3 | (set-option :produce-models true) 4 | (declare-fun sigmaStar_0 () String) 5 | (declare-fun sigmaStar_5 () String) 6 | (declare-fun x_2 () String) 7 | (declare-fun epsilon () String) 8 | (declare-fun literal_7 () String) 9 | (declare-fun x_6 () String) 10 | (declare-fun x_8 () String) 11 | (declare-fun sigmaStar_12 () String) 12 | (declare-fun literal_11 () String) 13 | (declare-fun x_14 () String) 14 | (declare-fun x_13 () String) 15 | (declare-fun x_15 () String) 16 | (declare-fun literal_16 () String) 17 | (declare-fun x_17 () String) 18 | (declare-fun x_18 () String) 19 | (declare-fun literal_19 () String) 20 | (declare-fun x_20 () String) 21 | 22 | (assert (= epsilon "")) 23 | (assert (or (= x_2 epsilon))) 24 | (assert (= literal_7 "[ hello:")) 25 | (assert (= x_8 (str.++ literal_7 x_6))) 26 | (assert (= x_18 (str.++ x_8 sigmaStar_0))) 27 | (assert (str.in.re x_18 (re.++ (re.* re.allchar) (re.++ (str.to.re "\. 17 | */ 18 | 19 | package strsolver 20 | 21 | object Flags { 22 | object ModelChecker extends Enumeration { 23 | val nuxmv, abc, simple = Value 24 | } 25 | 26 | var splitOptimization = false 27 | var minimalSuccessors = false 28 | var modelChecker= Set.empty[ModelChecker.Value] 29 | 30 | def isSplitOptimization: Boolean = splitOptimization 31 | def isMinimalSuccessors: Boolean = minimalSuccessors 32 | 33 | def isABC: Boolean = 34 | modelChecker(ModelChecker.abc) 35 | def isSimpleModelChecker: Boolean = 36 | modelChecker(ModelChecker.simple) 37 | } 38 | -------------------------------------------------------------------------------- /src/main/scala/RRFunsToAFA.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sloth, an SMT solver for strings. 3 | * Copyright (C) 2017 Philipp Ruemmer, Petr Janku 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package strsolver 20 | 21 | import ap.parser.IFunction 22 | import ap.parser.IExpression.Predicate 23 | 24 | import scala.collection.mutable 25 | 26 | 27 | object RRFunsToAFA { 28 | def addFun2AFA(fun : IFunction, afa : AFA) : Unit = 29 | fun2AFA.put(fun, afa) 30 | 31 | def addRel2Fun(p : Predicate, fun : IFunction) : Unit = 32 | rel2Fun.put(p, fun) 33 | 34 | def get(r : Predicate) : Option[AFA] = 35 | for (f <- rel2Fun get r; afa <- fun2AFA get f) yield afa 36 | 37 | private val fun2AFA = new mutable.HashMap[IFunction, AFA] 38 | private val rel2Fun = new mutable.HashMap[Predicate, IFunction] 39 | } 40 | -------------------------------------------------------------------------------- /tests/extract-1.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | ; transducer extracting the string in between the 0th and 1st '=' 4 | (define-funs-rec ((extract1st ((x String) (y String)) Bool) 5 | (extract1st_2 ((x String) (y String)) Bool) 6 | (extract1st_3 ((x String) (y String)) Bool)) ( 7 | 8 | ; extract1st 9 | (or (and (= x "") (= y "")) 10 | (and (not (= (seq-head x) (_ bv61 8))) ; not '=' 11 | (extract1st (seq-tail x) y)) 12 | (and (= (seq-head x) (_ bv61 8)) ; '=' 13 | (extract1st_2 (seq-tail x) y))) 14 | 15 | ; extract1st_2 16 | (or (and (= x "") (= y "")) 17 | (and (= (seq-head x) (seq-head y)) 18 | (not (= (seq-head x) (_ bv61 8))) ; not '=' 19 | (extract1st_2 (seq-tail x) (seq-tail y))) 20 | (and (= (seq-head x) (_ bv61 8)) ; '=' 21 | (extract1st_3 (seq-tail x) y))) 22 | 23 | ; extract1st_3 24 | (or (and (= x "") (= y "")) 25 | (extract1st_3 (seq-tail x) y)) 26 | 27 | )) 28 | 29 | (declare-fun x0 () String) 30 | (declare-fun x1 () String) 31 | (declare-fun x2 () String) 32 | (declare-fun x3 () String) 33 | (declare-fun x4 () String) 34 | (declare-fun s0 () String) ; source variable 35 | 36 | (assert (str.in.re s0 (re.* (re.range "x" "z")))) 37 | (assert (= x0 "abc=xyz=123=4")) 38 | 39 | (assert (extract1st x0 x1)) 40 | (assert (= x2 (str.++ x1 s0))) 41 | 42 | (assert (not (str.in.re x2 (re.* (re.range "x" "z"))))) 43 | 44 | (check-sat) 45 | 46 | -------------------------------------------------------------------------------- /tests/extract-1b.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | ; transducer extracting the string in between the 0th and 1st '=' 4 | (define-funs-rec ((extract1st ((x String) (y String)) Bool) 5 | (extract1st_2 ((x String) (y String)) Bool) 6 | (extract1st_3 ((x String) (y String)) Bool)) ( 7 | 8 | ; extract1st 9 | (or (and (= x "") (= y "")) 10 | (and (not (= (seq-head x) (_ bv61 8))) ; not '=' 11 | (extract1st (seq-tail x) y)) 12 | (and (= (seq-head x) (_ bv61 8)) ; '=' 13 | (extract1st_2 (seq-tail x) y))) 14 | 15 | ; extract1st_2 16 | (or (and (= x "") (= y "")) 17 | (and (= (seq-head x) (seq-head y)) 18 | (not (= (seq-head x) (_ bv61 8))) ; not '=' 19 | (extract1st_2 (seq-tail x) (seq-tail y))) 20 | (and (= (seq-head x) (_ bv61 8)) ; '=' 21 | (extract1st_3 (seq-tail x) y))) 22 | 23 | ; extract1st_3 24 | (or (and (= x "") (= y "")) 25 | (extract1st_3 (seq-tail x) y)) 26 | 27 | )) 28 | 29 | (declare-fun x0 () String) 30 | (declare-fun x1 () String) 31 | (declare-fun x2 () String) 32 | (declare-fun x3 () String) 33 | (declare-fun x4 () String) 34 | (declare-fun s0 () String) ; source variable 35 | 36 | (assert (str.in.re s0 (re.* (re.range "x" "z")))) 37 | (assert (str.in.re x0 (re.* (re.union (re.range "x" "z") (str.to.re "="))))) 38 | 39 | (assert (extract1st x0 x1)) 40 | (assert (= x2 (str.++ x1 s0))) 41 | 42 | (assert (not (str.in.re x2 (re.* (re.range "x" "z"))))) 43 | 44 | (check-sat) 45 | 46 | -------------------------------------------------------------------------------- /tests/runtests: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | SOLVER=../sloth 4 | 5 | TESTS="simple-cvc-smtlib.smt2 \ 6 | simple-cycle.smt2 simple-cycle2.smt2 \ 7 | nonlinear.smt2 nonlinear-2.smt2 \ 8 | norn-benchmark-9.smt2 \ 9 | norn-benchmark-9b.smt2 \ 10 | norn-benchmark-9c.smt2 \ 11 | norn-benchmark-9d.smt2 \ 12 | norn-benchmark-9e.smt2 \ 13 | norn-benchmark-9f.smt2 \ 14 | norn-benchmark-9g.smt2 \ 15 | norn-benchmark-9h.smt2 \ 16 | norn-benchmark-9i.smt2 \ 17 | norn-benchmark-9j.smt2 \ 18 | norn-benchmark-9k.smt2 \ 19 | simple-concat.smt2 \ 20 | simple-concat-2.smt2 \ 21 | simple-concat-3.smt2 \ 22 | simple-concat-4.smt2 \ 23 | simple-concat-4b.smt2 \ 24 | simple-concat-5.smt2 \ 25 | simple-concat-5b.smt2 \ 26 | concat.smt2 \ 27 | simple-replace.smt2 \ 28 | simple-replace-1b.smt2 \ 29 | simple-replace-1c.smt2 \ 30 | simple-replace-1d.smt2 \ 31 | simple-replace-1e.smt2 \ 32 | simple-replace-1f.smt2 \ 33 | simple-replace-1g.smt2 \ 34 | simple-replace-2.smt2 \ 35 | simple-replace-2b.smt2 \ 36 | simple-replace-2c.smt2 \ 37 | simple-replace-2d.smt2 \ 38 | simple-replace-3.smt2 \ 39 | simple-replace-3b.smt2 \ 40 | simple-replace-4.smt2 \ 41 | simple-replace-4b.smt2 \ 42 | simple-replace-4c.smt2 \ 43 | extract-1.smt2 \ 44 | extract-1b.smt2 \ 45 | empty-concat.smt2 \ 46 | escapeSequences-1a.smt2 \ 47 | escapeSequences-1b.smt2 \ 48 | epsilon-1.smt2 \ 49 | epsilon-2.smt2" 50 | 51 | for name in $TESTS; do 52 | echo 53 | echo $name 54 | $SOLVER "$@" $name 2>&1 # | grep -v 'time' 55 | done 56 | 57 | -------------------------------------------------------------------------------- /tests/cvc_replace_185.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | (set-option :strings-exp true) 3 | (set-option :produce-models true) 4 | (declare-fun sigmaStar_0 () String) 5 | (declare-fun sigmaStar_1 () String) 6 | (declare-fun literal_20 () String) 7 | (assert (= literal_20 "\x3c\x6c\x69\x3e\x3c\x69\x6d\x67\x20\x73\x72\x63\x3d\x5c\x22\x69\x6d\x61\x67\x65\x73\x2f\x69\x6d\x73\x2e\x67\x69\x66\x5c\x22\x20\x61\x6c\x74\x3d\x5c\x22\x49\x4d\x53\x20\x43\x50\x20\x50\x61\x63\x6b\x61\x67\x65\x5c\x22\x20\x2f\x3e\x20\x20\x28\x3c\x61\x20\x68\x72\x65\x66\x3d\x5c\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x0d\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x6f\x70\x65\x6e\x65\x72\x2e\x64\x6f\x63\x75\x6d\x65\x6e\x74\x2e\x66\x6f\x72\x6d\x73\x5b\x27\x66\x6f\x72\x6d\x27\x5d\x2e\x72\x65\x66\x65\x72\x65\x6e\x63\x65\x2e\x76\x61\x6c\x75\x65\x20\x3d\x20\x27\x23\x3b\x20\x0d\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x6f\x70\x65\x6e\x65\x72\x2e\x64\x6f\x63\x75\x6d\x65\x6e\x74\x2e\x66\x6f\x72\x6d\x73\x5b\x66\x6f\x72\x6d\x27\x5d\x2e\x6e\x61\x6d\x65\x2e\x76\x61\x6c\x75\x65\x20\x3d\x20\x27\x27\x3b\x20\x0d\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x77\x69\x6e\x64\x6f\x77\x2e\x63\x6c\x6f\x73\x65\x28\x29\x3b\x0d\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x5c\x22\x3e\x63\x68\x6f\x6f\x73\x65\x3c\x2f\x61\x3e\x29\x20\x28\x3c\x61\x20\x68\x72\x65\x66\x3d\x5c\x22\x70\x72\x65\x76\x69\x65\x77\x2e\x70\x68\x70\x3f\x64\x69\x72\x65\x63\x74\x6f\x72\x79\x3d\x5c\x22\x3e\x70\x72\x65\x76\x69\x65\x77\x3c\x2f\x61\x3e\x29\x3c\x2f\x6c\x69\x3e\x5c\x6e")) 8 | (assert (str.in.re literal_20 (re.++ (re.* re.allchar) (re.++ (str.to.re "\x5c\x3c\x53\x43\x52\x49\x50\x54") (re.* re.allchar))))) 9 | (check-sat) 10 | (get-model) 11 | -------------------------------------------------------------------------------- /src/main/scala/StringTheoryUtil.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sloth, an SMT solver for strings. 3 | * Copyright (C) 2017 Philipp Ruemmer, Petr Janku 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package strsolver 20 | 21 | import ap.terfor.Term 22 | 23 | object StringTheoryUtil { 24 | private var _numOfVariables: Int = -1; 25 | 26 | def setVariables(v: Int): Unit = _numOfVariables = v 27 | 28 | def getVariable: Int = { 29 | _numOfVariables += 1; _numOfVariables 30 | } 31 | 32 | private def createUnicodeChar(num: Int): String = { 33 | "\\" + new String(Character.toChars(num)) 34 | } 35 | 36 | private def getString(str: String) = 37 | """\\\\\\\\\\x\\([0-9a-f])\\([0-9a-f])""".r.replaceAllIn(str, { m => 38 | "\\" + Integer.parseInt(m.group(1) + m.group(2), 16).toChar 39 | }) 40 | 41 | private def createString(list: List[Either[Int, Term]]): String = list match { 42 | case Nil => "" 43 | case x :: xs => (x match { 44 | case Left(c) if (c > 39 && c < 44) || 45 | (c > 90 && c < 95) || 46 | c == 45 || 47 | c == 46 || 48 | c == 63 || 49 | c == 124 => c.toChar 50 | case Left(c) => "\\" + c.toChar//createUnicodeChar(c) 51 | case Right(s) => throw new Exception("Don't know how to handle " + s + " in createString") 52 | }) + createString(xs) 53 | } 54 | 55 | def createString(it: Iterator[List[Either[Int, Term]]]): String = { 56 | if (it.hasNext) { 57 | val str = createString(it.next()) 58 | 59 | """^\\/(?s)(.*)\\/$""".r.findFirstMatchIn(str) match { 60 | case None => 61 | str 62 | 63 | case Some(s) => 64 | s.group(1) 65 | } 66 | } else 67 | "" 68 | } 69 | } -------------------------------------------------------------------------------- /tests/concat2.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | ; to-uppercase transducer 4 | 5 | (define-fun-rec toUpper ((x String) (y String)) Bool 6 | (or (and (= x "") (= y "")) 7 | (and (= (seq-head y) 8 | (ite (and (bvule (_ bv97 8) (seq-head x)) ; 'a' 9 | (bvule (seq-head x) (_ bv122 8))) ; 'z' 10 | (bvsub (seq-head x) (_ bv32 8)) ; 'a' -> 'A', etc. 11 | (seq-head x))) 12 | (toUpper (seq-tail x) (seq-tail y)))) 13 | ) 14 | 15 | (define-funs-rec ((simple ((x String) (y String)) Bool) 16 | (state1 ((x String) (y String)) Bool) 17 | (state2 ((x String) (y String)) Bool) 18 | (state3 ((x String) (y String)) Bool) 19 | (state4 ((x String) (y String)) Bool) 20 | (state5 ((x String) (y String)) Bool)) ( 21 | 22 | ; definition of simple 23 | (and (= (seq-head x) (_ bv104 8)) ; 'h' 24 | (= (seq-head y) (_ bv119 8)) ; 'w' 25 | (state1 (seq-tail x) (seq-tail y))) 26 | 27 | ; definition of state1 28 | (and (= (seq-head x) (_ bv101 8)) ; 'e' 29 | (= (seq-head y) (_ bv111 8)) ; 'o' 30 | (state2 (seq-tail x) (seq-tail y))) 31 | 32 | ; definition of state2 33 | (and (= (seq-head x) (_ bv108 8)) ; 'l' 34 | (= (seq-head y) (_ bv114 8)) ; 'r' 35 | (state3 (seq-tail x) (seq-tail y))) 36 | 37 | ; definition of state3 38 | (and (= (seq-head x) (_ bv108 8)) ; 'l' 39 | (= (seq-head y) (_ bv108 8)) ; 'l' 40 | (state4 (seq-tail x) (seq-tail y))) 41 | 42 | ; definition of state4 43 | (and (= (seq-head x) (_ bv111 8)) ; 'o' 44 | (= (seq-head y) (_ bv100 8)) ; 'd' 45 | (state5 (seq-tail x) (seq-tail y))) 46 | 47 | ; definition of state5 48 | (and (= x "") (= y "")) 49 | )) 50 | 51 | (declare-fun x0 () String) 52 | (declare-fun x1 () String) 53 | (declare-fun x2 () String) 54 | (declare-fun x3 () String) 55 | (declare-fun x4 () String) 56 | (declare-fun x5 () String) 57 | (declare-fun s0 () String) ; source variable 58 | (declare-fun s1 () String) ; source variable 59 | 60 | (assert (= x0 (str.++ s0 s1))) 61 | (assert (= x1 (str.++ s1 s0))) 62 | (assert (simple x0 x2)) 63 | (assert (simple x1 x3)) 64 | 65 | (check-sat) 66 | -------------------------------------------------------------------------------- /tests/concat.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | ; to-uppercase transducer 4 | 5 | (define-fun-rec toUpper ((x String) (y String)) Bool 6 | (or (and (= x "") (= y "")) 7 | (and (= (seq-head y) 8 | (ite (and (bvule (_ bv97 8) (seq-head x)) ; 'a' 9 | (bvule (seq-head x) (_ bv122 8))) ; 'z' 10 | (bvsub (seq-head x) (_ bv32 8)) ; 'a' -> 'A', etc. 11 | (seq-head x))) 12 | (toUpper (seq-tail x) (seq-tail y)))) 13 | ) 14 | 15 | (define-funs-rec ((simple ((x String) (y String)) Bool) 16 | (state1 ((x String) (y String)) Bool) 17 | (state2 ((x String) (y String)) Bool) 18 | (state3 ((x String) (y String)) Bool) 19 | (state4 ((x String) (y String)) Bool) 20 | (state5 ((x String) (y String)) Bool)) ( 21 | 22 | ; definition of simple 23 | (and (= (seq-head x) (_ bv104 8)) ; 'h' 24 | (= (seq-head y) (_ bv119 8)) ; 'w' 25 | (state1 (seq-tail x) (seq-tail y))) 26 | 27 | ; definition of state1 28 | (and (= (seq-head x) (_ bv101 8)) ; 'e' 29 | (= (seq-head y) (_ bv111 8)) ; 'o' 30 | (state2 (seq-tail x) (seq-tail y))) 31 | 32 | ; definition of state2 33 | (and (= (seq-head x) (_ bv108 8)) ; 'l' 34 | (= (seq-head y) (_ bv114 8)) ; 'r' 35 | (state3 (seq-tail x) (seq-tail y))) 36 | 37 | ; definition of state3 38 | (and (= (seq-head x) (_ bv108 8)) ; 'l' 39 | (= (seq-head y) (_ bv108 8)) ; 'l' 40 | (state4 (seq-tail x) (seq-tail y))) 41 | 42 | ; definition of state4 43 | (and (= (seq-head x) (_ bv111 8)) ; 'o' 44 | (= (seq-head y) (_ bv100 8)) ; 'd' 45 | (state5 (seq-tail x) (seq-tail y))) 46 | 47 | ; definition of state5 48 | (and (= x "") (= y "")) 49 | )) 50 | 51 | (declare-fun x0 () String) 52 | (declare-fun x1 () String) 53 | (declare-fun x2 () String) 54 | (declare-fun x3 () String) 55 | (declare-fun x4 () String) 56 | (declare-fun s0 () String) ; source variable 57 | 58 | ; unsat? 59 | (assert (toUpper x2 x3)) 60 | (assert (= x0 (str.++ "he" s0 "o"))) 61 | (assert (= x2 (str.++ x0 x1))) 62 | (assert (not (str.in.re x4 (re.++ (re.++ (re.+ (re.range "a" "z")) (str.to.re " ")) (re.+ (re.range "A" "Z")))))) 63 | (assert (= x4 (str.++ x2 " " x3))) 64 | (assert (simple x0 x1)) 65 | 66 | (check-sat) 67 | -------------------------------------------------------------------------------- /tests/cvc_replace_4062.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | (set-option :strings-exp true) 3 | (set-option :produce-models true) 4 | (declare-fun sigmaStar_0 () String) 5 | (declare-fun literal_2 () String) 6 | (declare-fun x_7 () String) 7 | (declare-fun literal_3 () String) 8 | (declare-fun x_6 () String) 9 | (declare-fun literal_4 () String) 10 | (declare-fun literal_5 () String) 11 | (declare-fun x_12 () String) 12 | (declare-fun sigmaStar_13 () String) 13 | (declare-fun x_8 () String) 14 | (declare-fun epsilon () String) 15 | (declare-fun literal_11 () String) 16 | (declare-fun x_15 () String) 17 | (declare-fun literal_16 () String) 18 | (declare-fun x_17 () String) 19 | (declare-fun x_18 () String) 20 | (declare-fun x_14 () String) 21 | (declare-fun x_20 () String) 22 | (declare-fun literal_19 () String) 23 | (declare-fun x_22 () String) 24 | (declare-fun literal_21 () String) 25 | (declare-fun x_23 () String) 26 | (declare-fun literal_24 () String) 27 | (declare-fun x_26 () String) 28 | (declare-fun x_27 () String) 29 | (declare-fun literal_25 () String) 30 | (declare-fun x_28 () String) 31 | (declare-fun x_32 () String) 32 | (declare-fun literal_33 () String) 33 | (declare-fun x_34 () String) 34 | (declare-fun x_31 () String) 35 | (declare-fun x_35 () String) 36 | (declare-fun x_36 () String) 37 | (assert (= literal_2 "")) 38 | (assert (= x_7 (str.++ literal_2 sigmaStar_0))) 39 | (assert (= literal_3 " 40 | ")) 58 | (assert (= x_23 (str.++ x_20 literal_21))) 59 | (assert (= literal_24 " style=white-space:nowrap'>")) 60 | (assert (= x_26 (str.++ x_22 literal_24))) 61 | (assert (= x_27 (str.++ x_23 x_8))) 62 | (assert (= literal_25 " 63 | \n")) 67 | (assert (= x_34 (str.++ x_28 literal_33))) 68 | (assert (or (= x_31 epsilon))) 69 | (assert (= x_35 (str.++ x_32 x_31))) 70 | (assert (= x_36 (str.++ x_35 x_34))) 71 | (assert (str.in.re x_36 (re.++ (re.* re.allchar) (re.++ (str.to.re "\"$tempportfile" & 44 | 45 | nornId=$! 46 | 47 | sleep 1 48 | while [ `wc -l "$tempportfile" | awk '{ printf $1 }'` -lt 2 ]; do 49 | if ps -p $nornId >/dev/null; then 50 | sleep 1 51 | else 52 | echo "Could not start server" 53 | exit 1 54 | fi 55 | done 56 | 57 | mv "$tempportfile" "$portfile" 58 | rm "$lockfile" 59 | fi 60 | } 61 | 62 | ################################################################################ 63 | 64 | stdintoexitstatus() { 65 | read exitstatus 66 | return $exitstatus 67 | } 68 | 69 | if [ ! -f "$portfile" ]; then 70 | startDaemon 71 | fi 72 | 73 | mainProcess=$$ 74 | 75 | outputlogfile=`mktemp` 76 | 77 | success=1 78 | until [ $success -eq 0 ]; do 79 | 80 | port=`head -n1 "$portfile"` 81 | 82 | ( 83 | # send the ticket 84 | tail -n1 "$portfile" 85 | 86 | # command line arguments 87 | for var; do 88 | case "$var" in 89 | -*|+*) 90 | echo "$var" 91 | ;; 92 | *) 93 | echo `$pathCmd "$var"` 94 | ;; 95 | esac 96 | done 97 | 98 | echo "PROVE_AND_EXIT" 99 | 100 | # ping the daemon every second, to show that we are still 101 | # alive 102 | { 103 | sleep 1 104 | while ps -p $mainProcess >/dev/null; do 105 | echo "PING" 106 | sleep 1 107 | done 108 | } & 109 | ) | (((( nc localhost $port; echo $? >&3 ) | \ 110 | tee $outputlogfile >&4) 3>&1) | stdintoexitstatus) 4>&1 111 | 112 | success=$? 113 | 114 | if [ $success -ne 0 ]; then 115 | rm "$portfile" 116 | startDaemon 117 | else if grep -q '^ERROR:' $outputlogfile; then 118 | rm $outputlogfile 119 | exit 1 120 | fi; fi 121 | 122 | done 123 | 124 | rm $outputlogfile 125 | -------------------------------------------------------------------------------- /src/main/scala/SMTLIBStringTheory.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sloth, an SMT solver for strings. 3 | * Copyright (C) 2017 Philipp Ruemmer, Petr Janku 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package strsolver 20 | 21 | import ap._ 22 | import ap.parser._ 23 | import ap.terfor.conjunctions.Conjunction 24 | import ap.terfor.preds.Predicate 25 | import ap.theories._ 26 | 27 | object SMTLIBStringTheory extends Theory { 28 | 29 | override def toString = "SMTLIBStringTheory" 30 | 31 | // TODO: use proper sorts for the operations 32 | 33 | // Sequences 34 | 35 | val seq_unit = new IFunction("seq_unit", 1, true, false) 36 | val seq_empty = new IFunction("seq_empty", 0, true, false) 37 | val seq_concat = new IFunction("seq_concat", 2, true, false) 38 | 39 | val seq_cons = new IFunction("seq_cons", 2, true, false) 40 | val seq_rev_cons = new IFunction("seq_rev_cons", 2, true, false) 41 | val seq_head = new IFunction("seq_head", 1, true, false) 42 | val seq_tail = new IFunction("seq_tail", 1, true, false) 43 | val seq_last = new IFunction("seq_last", 1, true, false) 44 | val seq_first = new IFunction("seq_first", 1, true, false) 45 | 46 | val seq_prefix_of = new Predicate("seq_prefix_of", 2) 47 | val seq_suffix_of = new Predicate("seq_suffix_of", 2) 48 | val seq_subseq_of = new Predicate("seq_subseq_of", 2) 49 | 50 | val seq_extract = new IFunction("seq_extract", 3, true, false) 51 | val seq_nth = new IFunction("seq_nth", 2, true, false) 52 | 53 | val seq_length = new IFunction("seq_length", 1, true, false) 54 | 55 | val seq_replace = new IFunction("seq_replace", 3, true, false) 56 | val seq_replace_all = new IFunction("seq_replace_all", 3, true, false) 57 | 58 | // Regexes 59 | 60 | val re_empty_set = new IFunction("re_empty_set", 0, true, false) 61 | val re_full_set = new IFunction("re_full_set", 0, true, false) 62 | val re_allchar = new IFunction("re.allchar", 0, true, false) 63 | val re_concat = new IFunction("re_concat", 2, true, false) 64 | val re_of_seq = new IFunction("re_of_seq", 1, true, false) 65 | val re_empty_seq = new IFunction("re_empty_seq", 0, true, false) 66 | 67 | val re_star = new IFunction("re_star", 1, true, false) 68 | val re_loop = new IFunction("re_loop", 3, true, false) 69 | val re_plus = new IFunction("re_plus", 1, true, false) 70 | val re_option = new IFunction("re_option", 1, true, false) 71 | val re_range = new IFunction("re_range", 2, true, false) 72 | 73 | val re_union = new IFunction("re_union", 2, true, false) 74 | val re_difference = new IFunction("re_difference", 2, true, false) 75 | val re_intersect = new IFunction("re_intersect", 2, true, false) 76 | val re_complement = new IFunction("re_complement", 1, true, false) 77 | 78 | val re_of_pred = new IFunction("re_of_pred", 1, true, false) 79 | 80 | val re_member = new Predicate("re_member", 2) 81 | 82 | ////////////////////////////////////////////////////////////////////////////// 83 | 84 | val functions = List(seq_unit, seq_empty, seq_concat, 85 | seq_cons, seq_rev_cons, seq_head, seq_tail, seq_last, 86 | seq_first, seq_extract, seq_nth, seq_length, 87 | re_empty_set, re_full_set, re_allchar, re_concat, 88 | re_of_seq, re_empty_seq, 89 | re_star, re_loop, re_plus, re_option, re_range, 90 | re_union, re_difference, re_intersect, re_complement, 91 | re_of_pred, seq_replace, seq_replace_all) 92 | 93 | val (predicates, functionPredicateMapping, functionalPredicates) = { 94 | val functionEnc = new FunctionEncoder (true, false) 95 | val predicates = for (f <- functions) yield (functionEnc addFunction f) 96 | val allPredicates = 97 | List(seq_prefix_of, seq_suffix_of, seq_subseq_of, re_member) ::: predicates 98 | 99 | (allPredicates, 100 | functions zip predicates, 101 | predicates.toSet) 102 | } 103 | 104 | val axioms = Conjunction.TRUE 105 | val totalityAxioms = Conjunction.TRUE 106 | 107 | val predicateMatchConfig : Signature.PredicateMatchConfig = Map() 108 | val triggerRelevantFunctions : Set[IFunction] = functions.toSet 109 | 110 | def plugin = None 111 | 112 | TheoryRegistry register this 113 | 114 | } 115 | -------------------------------------------------------------------------------- /src/main/scala/ServerMain.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sloth, an SMT solver for strings. 3 | * Copyright (C) 2017 Philipp Ruemmer, Petr Janku 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package strsolver 20 | 21 | import ap.util.CmdlParser 22 | 23 | import scala.collection.mutable.ArrayBuffer 24 | import java.util.concurrent.Semaphore 25 | 26 | import java.net._ 27 | 28 | object ServerMain { 29 | 30 | private val InactivityTimeout = 30 * 60 * 1000 // shutdown after 30min inactivity 31 | private val TicketLength = 40 32 | private val MaxThreadNum = 1 // parts of the solver are not thread-safey yet! 33 | private val MaxWaitNum = 32 34 | 35 | private var lastActiveTime = System.currentTimeMillis 36 | private val serverSem = new Semaphore (MaxThreadNum) 37 | 38 | ////////////////////////////////////////////////////////////////////////////// 39 | 40 | def main(args : Array[String]) : Unit = { 41 | val predefPort = args match { 42 | case Array(CmdlParser.IntVal(v)) => Some(v) 43 | case _ => None 44 | } 45 | 46 | val socket = 47 | new ServerSocket(predefPort getOrElse 0, MaxWaitNum, 48 | InetAddress getByName "localhost") 49 | val port = socket.getLocalPort 50 | 51 | socket.setSoTimeout(InactivityTimeout / 2) 52 | 53 | Console.withOut(Console.err) { 54 | // println(lazabs.Main.greeting) 55 | println 56 | println("Daemon started on port " + port) 57 | } 58 | 59 | val r = new scala.util.Random 60 | val ticket = 61 | (for (_ <- 0 until TicketLength) yield r.nextPrintableChar) mkString "" 62 | 63 | println(port) 64 | println(ticket) 65 | 66 | //////////////////////////////////////////////////////////////////////////// 67 | // The main loop 68 | 69 | var serverRunning = true 70 | while (serverRunning) { 71 | 72 | // Get a token to serve another request 73 | serverSem.acquire 74 | 75 | try { 76 | val clientSocket = socket.accept 77 | 78 | val thread = new Thread(new Runnable { def run : Unit = { 79 | Console setErr ap.CmdlMain.NullStream 80 | 81 | val inputReader = 82 | new java.io.BufferedReader( 83 | new java.io.InputStreamReader(clientSocket.getInputStream)) 84 | 85 | val receivedTicket = inputReader.readLine 86 | if (ticket == receivedTicket) { 87 | val arguments = new ArrayBuffer[String] 88 | 89 | var str = inputReader.readLine 90 | var done = false 91 | while (!done && str != null) { 92 | str.trim match { 93 | case "PROVE_AND_EXIT" => { 94 | done = true 95 | Console.withOut(clientSocket.getOutputStream) { 96 | var checkNum = 0 97 | var lastPing = System.currentTimeMillis 98 | var cancel = false 99 | 100 | SMTLIBMain.doMain(arguments.toArray, { 101 | checkNum = checkNum + 1 102 | cancel || (checkNum % 50 == 0 && { 103 | val currentTime = System.currentTimeMillis 104 | while (inputReader.ready) { 105 | inputReader.read 106 | lastPing = currentTime 107 | } 108 | cancel = currentTime - lastPing > 3000 109 | cancel 110 | }) 111 | }) 112 | } 113 | } 114 | case str => 115 | arguments += str 116 | } 117 | 118 | if (!done) 119 | str = inputReader.readLine 120 | } 121 | } 122 | 123 | inputReader.close 124 | 125 | // Put back the token 126 | lastActiveTime = System.currentTimeMillis 127 | serverSem.release 128 | }}) 129 | 130 | thread.start 131 | 132 | } catch { 133 | case _ : SocketTimeoutException => { 134 | // check whether any other thread is still active 135 | serverSem.release 136 | 137 | if (serverSem.availablePermits == MaxThreadNum && 138 | System.currentTimeMillis - lastActiveTime > InactivityTimeout) { 139 | Console.err.println("Shutting down inactive solver daemon") 140 | serverRunning = false 141 | } 142 | } 143 | } 144 | } 145 | 146 | } 147 | 148 | } -------------------------------------------------------------------------------- /src/main/scala/StringPreprocessing.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sloth, an SMT solver for strings. 3 | * Copyright (C) 2017 Philipp Ruemmer, Petr Janku 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package strsolver 20 | 21 | import ap.basetypes.IdealInt 22 | import ap.proof.goal.Goal 23 | import ap.terfor.{ConstantTerm, Term} 24 | import ap.terfor.preds.{Atom, PredConj} 25 | 26 | import scala.collection.mutable.LinkedHashSet 27 | 28 | object StringPreprocessing { 29 | import StringTheory._ 30 | 31 | private val p = functionPredicateMap 32 | private val wordVariables = new LinkedHashSet[Term] 33 | private val sigmaStars = new LinkedHashSet[Term] 34 | private val stringVariables = new LinkedHashSet[Term] 35 | 36 | private def getDiffAtoms(goal: Goal) = { 37 | import ap.terfor.TerForConvenience._ 38 | implicit val _ = goal.order 39 | 40 | val newAtoms = new LinkedHashSet[Atom] 41 | val variables = wordVariables -- stringVariables 42 | var oldSize = -1 43 | 44 | while (newAtoms.size > oldSize) { 45 | oldSize = newAtoms.size 46 | for (Seq((IdealInt.ONE, c : ConstantTerm), 47 | (IdealInt.MINUS_ONE, d : ConstantTerm)) <- 48 | goal.facts.arithConj.negativeEqs; 49 | lc = l(c); 50 | ld = l(d); 51 | if ((variables contains lc) || 52 | (variables contains ld)) && 53 | !((sigmaStars contains lc) || 54 | (sigmaStars contains ld))) { 55 | newAtoms += wordDiff(List(lc, ld)) 56 | wordVariables += lc 57 | wordVariables += ld 58 | } 59 | } 60 | 61 | newAtoms 62 | } 63 | 64 | def apply(goal: Goal) = { 65 | val atoms = goal.facts.predConj 66 | val regex2AFA = new Regex2AFA(atoms) 67 | val transVariables = new LinkedHashSet[Term] 68 | val constraintDeps = new LinkedHashSet[Term] 69 | var regularVariables = List.empty[Term] 70 | val constraintPositiveAtoms = new LinkedHashSet[Atom] 71 | val constraintNegativeAtoms = new LinkedHashSet[Atom] 72 | val wordAtoms = new LinkedHashSet[Atom] 73 | 74 | atoms.positiveLits foreach { 75 | case a if a.pred == p(replace) || a.pred == p(replaceall) => 76 | wordVariables ++= Iterator(a(0), a(3)) 77 | transVariables ++= Iterator(a(0), a(3)) 78 | constraintDeps += a.head 79 | constraintPositiveAtoms += a 80 | 81 | case a if a.pred == member => 82 | wordVariables += a.head 83 | regularVariables ::= a.head 84 | constraintDeps += a.head 85 | constraintPositiveAtoms += a 86 | 87 | case a if a.pred == p(wordCat) => 88 | wordVariables += a.last 89 | sigmaStars ++= a.init 90 | stringVariables ++= a.iterator 91 | wordAtoms += a 92 | 93 | case a if a.pred == p(wordChar) => 94 | wordVariables += a.last 95 | stringVariables += a.last 96 | wordAtoms += a 97 | 98 | case a if a.pred == p(wordEps) => 99 | wordVariables += a.head 100 | stringVariables += a.head 101 | wordAtoms += a 102 | 103 | case a if (RRFunsToAFA get a.pred).isDefined => 104 | wordVariables ++= a.init 105 | transVariables ++= a.init 106 | constraintDeps += a.head 107 | constraintPositiveAtoms += a 108 | 109 | case _ => // nothing 110 | } 111 | 112 | constraintPositiveAtoms ++= getDiffAtoms(goal) 113 | atoms.negativeLits foreach { 114 | case a if a.pred == member => 115 | wordVariables += a.head 116 | regularVariables ::= a.head 117 | constraintDeps += a.head 118 | constraintNegativeAtoms += a 119 | 120 | case a => // nothing 121 | throw new Exception("Unexpected atom: " + a) 122 | } 123 | 124 | sigmaStars --= wordVariables 125 | 126 | var vars = Set.empty[Term] 127 | val withConcat = !(constraintDeps forall {v => 128 | val string = regex2AFA.buildStrings(v) 129 | 130 | if(string.hasNext) { 131 | string.next.forall { 132 | case Right(x) if x == v => 133 | true 134 | case Left(x) => 135 | true 136 | case Right(x) => 137 | val res = sigmaStars(x) && !(vars contains x) 138 | vars += x 139 | res 140 | } 141 | } 142 | else 143 | throw new Exception("Unexpected situation") 144 | }) 145 | 146 | (PredConj(constraintPositiveAtoms.toList, constraintNegativeAtoms.toList, goal.order), 147 | wordVariables ++ sigmaStars, 148 | PredConj(wordAtoms.toList, List(), goal.order), 149 | withConcat) 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /tests/transducer3.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_S) 2 | 3 | ; ---------------- 4 | ; EscapeString by Dunkey 5 | ; ----------------- 6 | ; & = & 7 | ; < = < 8 | ; > = > 9 | ; " = " 10 | ; ' = ' 11 | 12 | (define-funs-rec ((escapeString ((x String) (y String)) Bool) 13 | (es1 ((x String) (y String)) Bool) 14 | (es2 ((x String) (y String)) Bool) 15 | (es3 ((x String) (y String)) Bool) 16 | (es4 ((x String) (y String)) Bool) 17 | (es5 ((x String) (y String)) Bool) 18 | (es6 ((x String) (y String)) Bool) 19 | (es7 ((x String) (y String)) Bool) 20 | (es8 ((x String) (y String)) Bool) 21 | (es9 ((x String) (y String)) Bool) 22 | (es10 ((x String) (y String)) Bool) 23 | (es11 ((x String) (y String)) Bool) 24 | (es12 ((x String) (y String)) Bool) 25 | (es13 ((x String) (y String)) Bool) 26 | (es14 ((x String) (y String)) Bool) 27 | ) ( 28 | 29 | ; definition of escapeString 30 | (or (and (= (seq-head x) (_ bv38 8)) ; '&' 31 | (= (seq-head y) (_ bv38 8)) ; '&' 32 | (es1 (seq-tail x) (seq-tail y))) 33 | (and (= (seq-head x) (_ bv60 8)) ; '<' 34 | (= (seq-head y) (_ bv38 8)) ; '&' 35 | (es2 (seq-tail x) (seq-tail y))) 36 | (and (= (seq-head x) (_ bv62 8)) ; '>' 37 | (= (seq-head y) (_ bv38 8)) ; '&' 38 | (es3 (seq-tail x) (seq-tail y))) 39 | (and (= (seq-head x) (_ bv34 8)) ; '\"' 40 | (= (seq-head y) (_ bv38 8)) ; '&' 41 | (es4 (seq-tail x) (seq-tail y))) 42 | (and (= (seq-head x) (_ bv39 8)) ; '\'' 43 | (= (seq-head y) (_ bv38 8)) ; '&' 44 | (es5 (seq-tail x) (seq-tail y))) 45 | (and (= (seq-head x) (seq-head y)) 46 | (not (= (seq-head x) (_ bv39 8))) ; '\'' 47 | (not (= (seq-head x) (_ bv38 8))) ; '&' 48 | (not (= (seq-head x) (_ bv60 8))) ; '<' 49 | (not (= (seq-head x) (_ bv62 8))) ; '>' 50 | (not (= (seq-head x) (_ bv34 8))) ; '\"' 51 | (not (= (seq-head x) (_ bv39 8))) ; '\'' 52 | (escapeString (seq-tail x) (seq-tail y)))) 53 | 54 | ; definition of es1 55 | (or (and (= (seq-head y) (_ bv97 8)) ; 'a' 56 | (es6 x (seq-tail y)))) 57 | 58 | ; definition of es2 59 | (or (and (= (seq-head y) (_ bv108 8)) ; 'l' 60 | (es12 x (seq-tail y)))) 61 | 62 | ; definition of es3 63 | (or (and (= (seq-head y) (_ bv103 8)) ; 'g' 64 | (es12 x (seq-tail y)))) 65 | 66 | ; definition of es4 67 | (or (and (= (seq-head y) (_ bv113 8)) ; 'q' 68 | (es7 x (seq-tail y)))) 69 | 70 | ; definition of es5 71 | (or (and (= (seq-head y) (_ bv35 8)) ; '#' 72 | (es8 x (seq-tail y)))) 73 | 74 | ; definition of es6 75 | (or (and (= (seq-head y) (_ bv109 8)) ; 'm' 76 | (es9 x (seq-tail y)))) 77 | 78 | ; definition of es7 79 | (or (and (= (seq-head y) (_ bv117 8)) ; 'u' 80 | (es10 x (seq-tail y)))) 81 | 82 | ; definition of es8 83 | (or (and (= (seq-head y) (_ bv51 8)) ; '3' 84 | (es11 x (seq-tail y)))) 85 | 86 | ; definition of es9 87 | (or (and (= (seq-head y) (_ bv112 8)) ; 'p' 88 | (es13 x (seq-tail y)))) 89 | 90 | ; definition of es10 91 | (or (and (= (seq-head y) (_ bv111 8)) ; 'o' 92 | (es12 x (seq-tail y)))) 93 | 94 | ; definition of es11 95 | (or (and (= (seq-head y) (_ bv57 8)) ; '9' 96 | (es13 x (seq-tail y)))) 97 | 98 | ; definition of es12 99 | (or (and (= (seq-head y) (_ bv116 8)) ; 't' 100 | (es13 x (seq-tail y)))) 101 | 102 | ; definition of es13 103 | (or (and (= (seq-head y) (_ bv59 8)) ; ';' 104 | (es14 x (seq-tail y)))) 105 | 106 | ; definition of es14 107 | (and (= x "") (= y "")) 108 | )) 109 | 110 | (declare-fun x () String) 111 | (declare-fun y () String) 112 | 113 | (assert (str.in.re x (re.+ (re.union (re.union (re.union (str.to.re "&") (str.to.re "\\'")) (str.to.re "<")) (str.to.re ">"))))) 114 | ;(assert (escapeString x y)) 115 | 116 | (check-sat) 117 | -------------------------------------------------------------------------------- /src/main/scala/SimpleModelChecker.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sloth, an SMT solver for strings. 3 | * Copyright (C) 2017 Philipp Ruemmer, Petr Janku 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package strsolver 20 | 21 | import org.sat4j.minisat.SolverFactory 22 | import org.sat4j.core.{Vec, VecInt} 23 | import org.sat4j.specs.{IVecInt} 24 | import org.sat4j.tools.{ModelIterator} 25 | 26 | import scala.collection.mutable 27 | 28 | 29 | object SimpleModelChecker { 30 | type Clauses = mutable.LinkedHashSet[IVecInt] 31 | 32 | val solver = SolverFactory.newDefault() 33 | val finalCond = SolverFactory.newDefault() 34 | var formula2lit = Map.empty[AFormula, (Int, Clauses)] 35 | var lit2state = Map.empty[Int, Int] 36 | 37 | private def update(af: AFormula, clauses: Clauses = mutable.LinkedHashSet.empty[IVecInt]) = { 38 | val res = formula2lit.size + 1 39 | 40 | formula2lit += af -> (res, clauses) 41 | 42 | res 43 | } 44 | 45 | private def decodeRange(af: AFormula, vars: IndexedSeq[AFormula]): AFormula = af match { 46 | case AFLet(definition, in) => 47 | decodeRange(in, decodeRange(definition, vars) +: vars) 48 | 49 | case AFAnd(sub1, sub2) => decodeRange(sub1, vars) & decodeRange(sub2, vars) 50 | case AFOr(sub1, sub2) => decodeRange(sub1, vars) | decodeRange(sub2, vars) 51 | case AFNot(sub) => ~decodeRange(sub, vars) 52 | case AFDeBrujinVar(ind) => vars(ind) 53 | case s => s 54 | } 55 | 56 | private def buildClauses(af: AFormula): (Int, Clauses) = af match { 57 | case and@AFAnd(sub1, sub2) => 58 | val (lit1, cl1) = formula2lit getOrElse(sub1, buildClauses(sub1)) 59 | val (lit2, cl2) = formula2lit getOrElse(sub2, buildClauses(sub2)) 60 | val clauses = cl1 ++ cl2 61 | val lit = update(and, clauses) 62 | 63 | clauses += new VecInt(Array(-lit, lit1)) 64 | clauses += new VecInt(Array(-lit, lit2)) 65 | clauses += new VecInt(Array(lit, -lit1, -lit2)) 66 | 67 | (lit, clauses) 68 | 69 | case or@AFOr(sub1, sub2) => 70 | val (lit1, cl1) = formula2lit getOrElse(sub1, buildClauses(sub1)) 71 | val (lit2, cl2) = formula2lit getOrElse(sub2, buildClauses(sub2)) 72 | val clauses = cl1 ++ cl2 73 | val lit = update(or, clauses) 74 | 75 | clauses += new VecInt(Array(lit, -lit1)) 76 | clauses += new VecInt(Array(lit, -lit2)) 77 | clauses += new VecInt(Array(-lit, lit1, lit2)) 78 | 79 | (lit, clauses) 80 | 81 | case l:AFLet => 82 | val formula = decodeRange(l, IndexedSeq.empty) 83 | 84 | formula2lit getOrElse(formula, buildClauses(formula)) 85 | 86 | case AFNot(sub) => 87 | val (lit, cl) = formula2lit getOrElse(sub, buildClauses(sub)) 88 | 89 | (-lit, cl) 90 | 91 | case s@AFStateVar(v) => 92 | val clauses = mutable.LinkedHashSet.empty[IVecInt] 93 | val lit = update(s, clauses) 94 | 95 | lit2state += lit -> v 96 | 97 | (lit, clauses) 98 | 99 | case v => 100 | val clauses = mutable.LinkedHashSet.empty[IVecInt] 101 | 102 | (update(v, clauses), clauses) 103 | } 104 | 105 | private def xor(vars: Set[Int], clauses: Clauses) = if(vars.size > 1) { 106 | var tail = vars 107 | var res: Clauses = mutable.LinkedHashSet.empty 108 | 109 | res += new VecInt(vars.toArray) 110 | 111 | for (x <- vars.init) { 112 | tail = tail.tail 113 | for (y <- tail) { 114 | res += new VecInt(Array(-x, -y)) 115 | } 116 | } 117 | 118 | clauses ++= res 119 | } 120 | 121 | private def buildClausesFromFormula(f: AFormula) = { 122 | (formula2lit getOrElse(f, { 123 | val (lit, cl) = buildClauses(f) 124 | val clauses = cl + new VecInt(Array(lit)) 125 | 126 | (lit, clauses) 127 | }))._2 128 | } 129 | 130 | private def nextStates(lits: Array[Int]) = { 131 | var res = Set.empty[Int] 132 | 133 | for (lit <- lits) lit2state get lit match { 134 | case None => 135 | case Some(s) => res += s 136 | } 137 | 138 | res 139 | } 140 | 141 | def apply(afa: AFA): Boolean = { 142 | solver.reset 143 | solver.newVar(100000) 144 | finalCond.reset 145 | finalCond.newVar(100000) 146 | formula2lit = Map.empty[AFormula, (Int, Clauses)] 147 | lit2state = Map.empty[Int, Int] 148 | 149 | val transitions = afa.states map {s => 150 | val clauses = buildClausesFromFormula(s) 151 | 152 | xor(s.getStates.map(s => formula2lit(AFStateVar(s))._1), clauses) 153 | new Vec[IVecInt](clauses.toArray) 154 | } 155 | 156 | 157 | var workList = mutable.LinkedHashSet[Set[Int]]() 158 | val initSolver = SolverFactory.newDefault() 159 | val initConstr = new VecInt() 160 | 161 | initSolver.reset 162 | initSolver.newVar(100000) 163 | initSolver.addAllClauses(new Vec[IVecInt](buildClausesFromFormula(afa.initialStates).toArray)) 164 | 165 | val initIt = new ModelIterator(initSolver) 166 | while (initIt.isSatisfiable(initConstr)) { 167 | val model = initIt.model 168 | val states = nextStates(model) 169 | 170 | workList += states 171 | } 172 | 173 | val it = new ModelIterator(solver) 174 | val size = workList.head.size 175 | val visited = mutable.LinkedHashSet.empty[Set[Int]] 176 | 177 | finalCond.addAllClauses(new Vec[IVecInt](buildClausesFromFormula(afa.finalStates).toArray)) 178 | while(workList.nonEmpty) { 179 | val states = workList.last 180 | var constr = new VecInt() 181 | val finalAssumps = new VecInt((states map { s => formula2lit(AFStateVar(s))._1 }).toArray) 182 | 183 | visited += states 184 | workList = workList.init 185 | if (states.size == size && finalCond.isSatisfiable(finalAssumps)) { 186 | return true 187 | } 188 | 189 | 190 | solver.reset 191 | states foreach(s => solver.addAllClauses(transitions(s))) 192 | while (it.isSatisfiable) { 193 | val model = it.model 194 | val next = nextStates(model) 195 | 196 | constr = new VecInt((next map { s => -formula2lit(AFStateVar(s))._1 }).toArray) 197 | solver.addClause(constr) 198 | if (next.size == size && !workList(next) && !visited(next)) { 199 | workList += next 200 | } 201 | } 202 | } 203 | 204 | false 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /src/main/scala/Regex2AFA.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sloth, an SMT solver for strings. 3 | * Copyright (C) 2017 Philipp Ruemmer, Petr Janku 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package strsolver 20 | 21 | import ap.terfor.Term 22 | import ap.terfor.preds.{Predicate, Atom, PredConj} 23 | 24 | import dk.brics.automaton.{BasicAutomata, BasicOperations, RegExp, 25 | Automaton} 26 | 27 | import scala.collection.JavaConverters._ 28 | import scala.collection.mutable.ArrayBuffer 29 | 30 | class Regex2AFA(atoms : PredConj) { 31 | import StringTheory._ 32 | private val p = functionPredicateMap 33 | 34 | private def numToUnicode(num : Int) : String = 35 | new String(Character.toChars(num)) 36 | 37 | def buildAFA(c : Term) : AFA = { 38 | val aut = buildBricsAut(c) 39 | // println(aut) 40 | brics2AFA(aut) 41 | } 42 | 43 | def buildComplAFA(c : Term) : AFA = { 44 | val aut = buildBricsAut(c) 45 | // println(aut) 46 | // println("Complementing ...") 47 | val complAut = BasicOperations complement aut 48 | // println(complAut) 49 | brics2AFA(complAut) 50 | } 51 | 52 | ////////////////////////////////////////////////////////////////////////////// 53 | 54 | def buildRegex(c : Term) : String = 55 | (for (a <- atoms positiveLitsWithPred p(rexEmpty); if (a(0) == c)) 56 | yield "#").headOption orElse 57 | (for (a <- atoms positiveLitsWithPred p(rexEps); if (a(0) == c)) 58 | yield "()").headOption orElse 59 | (for (a <- atoms positiveLitsWithPred p(rexSigma); if (a(0) == c)) 60 | yield ".").headOption orElse 61 | (for (a <- atoms positiveLitsWithPred p(rexChar); if (a(1) == c)) 62 | yield { 63 | assert(a(0).isConstant) 64 | "\\" + numToUnicode(a(0).constant.intValueSafe) 65 | }).headOption orElse 66 | (for (a <- atoms positiveLitsWithPred p(rexRange); if (a(2) == c)) 67 | yield { 68 | assert(a(0).isConstant) 69 | assert(a(1).isConstant) 70 | "[\\" + numToUnicode(a(0).constant.intValueSafe) + "-" + 71 | "\\" + numToUnicode(a(1).constant.intValueSafe) + "]" 72 | }).headOption orElse 73 | (for (a <- atoms positiveLitsWithPred p(rexCat); if (a(2) == c)) 74 | yield (buildRegex(a(0)) + buildRegex(a(1)))).headOption orElse 75 | (for (a <- atoms positiveLitsWithPred p(rexUnion); if (a(2) == c)) 76 | yield ("(" + buildRegex(a(0)) + "|" + buildRegex(a(1)) + ")")).headOption orElse 77 | (for (a <- atoms positiveLitsWithPred p(rexStar); if (a(1) == c)) 78 | yield ("(" + buildRegex(a(0)) + ")*")).headOption orElse 79 | (for (a <- atoms positiveLitsWithPred p(rexNeg); if (a(1) == c)) 80 | yield ("~" + buildRegex(a(0)))).headOption getOrElse { 81 | assert(false) 82 | null 83 | } 84 | 85 | ////////////////////////////////////////////////////////////////////////////// 86 | // Reconstruct strings from wordEps, wordChar, wordCat 87 | 88 | def buildStrings(c : Term) : Iterator[List[Either[Int, Term]]] = { 89 | val definitions = 90 | (for (a <- (atoms positiveLitsWithPred p(wordEps)).iterator; 91 | if (a(0) == c)) 92 | yield List()) ++ 93 | (for (a <- (atoms positiveLitsWithPred p(wordChar)).iterator; 94 | if (a(0).isConstant && a(1) == c)) 95 | yield { 96 | List(Left(a(0).constant.intValueSafe)) 97 | }) ++ 98 | (for (a <- (atoms positiveLitsWithPred p(wordCat)).iterator; 99 | if (a(2) == c); 100 | left <- buildStrings(a(0)); 101 | right <- buildStrings(a(1))) 102 | yield (left ::: right)) 103 | if (definitions.hasNext) 104 | definitions 105 | else 106 | Iterator single List(Right(c)) 107 | } 108 | 109 | /** 110 | * Convert a concrete string (possibly including word variables) 111 | * to an AFA. A word variable is simply interpreted as representing 112 | * arbitrary words, ignoring further constraints that might exist 113 | * on the variable. 114 | */ 115 | def string2AFA(string : List[Either[Int, Term]]) : AFA = { 116 | val stateFors = new ArrayBuffer[AFormula] 117 | for (el <- string) el match { 118 | case Left(char) => 119 | stateFors += 120 | AFStateVar(stateFors.size + 1) & ~AFSpecSymb(0) & AFormula.createSymbol(char, 0) 121 | case Right(v) => 122 | stateFors += 123 | AFStateVar(stateFors.size) | AFStateVar(stateFors.size + 1) 124 | } 125 | stateFors += AFFalse 126 | val finalStates = AFormula.and(for (n <- 0 until (stateFors.size - 1)) 127 | yield ~AFStateVar(n)) 128 | new AFA(AFStateVar(0), stateFors.toVector, finalStates) 129 | } 130 | 131 | def extractEqConstraints(x : Term) : Option[AFA] = { 132 | val allStrings = 133 | for (s <- buildStrings(x); if s != List(Right(x))) yield s 134 | if (allStrings.hasNext) 135 | Some((for (s <- allStrings) yield string2AFA(s)) reduceLeft 136 | (AFA.synchronise(_, _, 0))) 137 | else 138 | None 139 | } 140 | 141 | ////////////////////////////////////////////////////////////////////////////// 142 | 143 | def buildBricsAut(c : Term) : Automaton = 144 | new RegExp(buildRegex(c)).toAutomaton 145 | 146 | private def brics2AFA(aut : Automaton) : AFA = { 147 | val initState = aut.getInitialState 148 | val states = initState :: (aut.getStates.asScala - initState).toList 149 | val stateInd = states.iterator.zipWithIndex.toMap 150 | 151 | val transFors = for (state <- states) yield AFormula.or( 152 | for (trans <- 153 | (state getSortedTransitions false).iterator.asScala) yield { 154 | val targetConstr = AFStateVar(stateInd(trans.getDest)) 155 | val labelConstr = 156 | if (trans.getMin == trans.getMax) 157 | ~AFSpecSymb(0) & AFormula.createSymbol(trans.getMin, 0) 158 | else 159 | ~AFSpecSymb(0) & AFormula.symbolInRange(trans.getMin, trans.getMax) 160 | targetConstr & labelConstr 161 | }) 162 | 163 | val finalStates = 164 | AFormula.and(for ((state, num) <- states.iterator.zipWithIndex; 165 | if !state.isAccept) 166 | yield ~AFStateVar(num)) 167 | 168 | new AFA(AFStateVar(0), transFors.toVector, finalStates) 169 | } 170 | 171 | 172 | } -------------------------------------------------------------------------------- /src/main/scala/SMTLIBMain.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sloth, an SMT solver for strings. 3 | * Copyright (C) 2017 Philipp Ruemmer, Petr Janku 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package strsolver 20 | 21 | import ap._ 22 | import ap.parser._ 23 | import ap.parameters.PreprocessingSettings 24 | import ap.util.CmdlParser 25 | import scala.collection.mutable.ArrayBuffer 26 | import ap.theories.TheoryRegistry 27 | 28 | object SMTLIBMain { 29 | 30 | class MainException(msg : String) extends Exception(msg) 31 | object TimeoutException extends MainException("timeout") 32 | object StoppedException extends MainException("stopped") 33 | 34 | def doMain(args: Array[String], 35 | stoppingCond : => Boolean) : Unit = try { 36 | val filenames = new ArrayBuffer[String] 37 | var timeout : Option[Long] = None 38 | var model = false 39 | var assertions = false 40 | 41 | for (str <- args) str match { 42 | case CmdlParser.ValueOpt("timeout", value) => 43 | timeout = Some(value.toLong * 1000) 44 | case CmdlParser.Opt("model", value) => 45 | model = value 46 | case CmdlParser.Opt("assert", value) => 47 | assertions = value 48 | case CmdlParser.Opt("splitOpt", value) => 49 | Flags.splitOptimization = value 50 | case CmdlParser.ValueOpt("modelChecker", mcs) => 51 | try { 52 | mcs.split(",").foreach { mc => 53 | val modelChecker = Flags.ModelChecker withName mc 54 | 55 | if (Flags.isABC && modelChecker == Flags.ModelChecker.nuxmv) 56 | Flags.modelChecker -= Flags.ModelChecker.abc 57 | 58 | Flags.modelChecker += modelChecker 59 | } 60 | } catch { 61 | case _ : NoSuchElementException => 62 | throw new Exception("unknown model checker") 63 | } 64 | case CmdlParser.Opt("minimalSuccessors", value) => 65 | Flags.minimalSuccessors = value 66 | case str => 67 | filenames += str 68 | } 69 | 70 | if (filenames.size != 1) 71 | throw new Exception("expected a single filename as argument") 72 | 73 | val startTime = System.currentTimeMillis 74 | 75 | val timeoutChecker = timeout match { 76 | case Some(to) => () => { 77 | if (System.currentTimeMillis - startTime > to) 78 | throw TimeoutException 79 | if (stoppingCond) 80 | throw StoppedException 81 | } 82 | case None => () => { 83 | if (stoppingCond) 84 | throw StoppedException 85 | } 86 | } 87 | 88 | ap.util.Debug.enableAllAssertions(assertions) 89 | 90 | //////////////////////////////////////////////////////////////////////////// 91 | 92 | val fileName = filenames.head 93 | Console.err.println("Reading file " + fileName + " ...") 94 | 95 | val reader = new SMTReader(fileName) 96 | val bitwidth = reader.bitwidth 97 | if (reader.includesGetModel) 98 | model = true 99 | 100 | // just handle the problem using a normal solver 101 | val functionEnc = 102 | new FunctionEncoder (true, false) 103 | for (t <- reader.signature.theories) 104 | functionEnc addTheory t 105 | 106 | val (List(INamedPart(_, formula)), _, signature) = 107 | Preprocessing(reader.rawFormula, 108 | List(), 109 | reader.signature, 110 | PreprocessingSettings.DEFAULT, 111 | functionEnc) 112 | 113 | // tell the AFA store about introduced relations 114 | for ((p, f) <- functionEnc.predTranslation) 115 | if ((TheoryRegistry lookupSymbol f).isEmpty) 116 | RRFunsToAFA.addRel2Fun(p, f) 117 | 118 | val formulaWithoutQuans = SMTReader.eliminateUniQuantifiers(formula) 119 | val intFormula = StringTheoryTranslator(formulaWithoutQuans, 120 | reader.wordConstants) 121 | 122 | SimpleAPI.withProver(enableAssert = assertions) { p => 123 | import IExpression._ 124 | import SimpleAPI._ 125 | import p._ 126 | 127 | try { 128 | addConstantsRaw(SymbolCollector constantsSorted intFormula) 129 | addRelations(for (p <- signature.order.orderedPredicates.toSeq sortBy (_.name); 130 | if ((TheoryRegistry lookupSymbol p).isEmpty)) 131 | yield p) 132 | 133 | ?? (intFormula) 134 | 135 | Console.err.println 136 | val res = { 137 | checkSat(false) 138 | while (getStatus(100) == ProverStatus.Running) 139 | timeoutChecker() 140 | ??? 141 | } 142 | 143 | res match { 144 | case ProverStatus.Valid => println("unsat") 145 | case ProverStatus.Inconclusive => println("unknown") 146 | case ProverStatus.OutOfMemory => println("OOM") 147 | case ProverStatus.Invalid => { 148 | println("sat") 149 | 150 | if (model) { 151 | Console.err.println 152 | for (c <- reader.wordConstants) 153 | for (v <- evalPartial(c)) { 154 | print("(define-fun " + 155 | (SMTLineariser quoteIdentifier c.name) + 156 | " () String ") 157 | println("\"" + 158 | SMTLIBStringParser.escapeString( 159 | StringTheory.asString(v)(decoderContext)) + 160 | "\")") 161 | } 162 | for (c <- reader.otherConstants) 163 | for (v <- evalPartial(c)) { 164 | print("(define-fun " + 165 | (SMTLineariser quoteIdentifier c.name) + 166 | " () Int ") 167 | println("" + v + ")") 168 | } 169 | } 170 | } 171 | } 172 | 173 | } finally { 174 | // Make sure that the prover actually stops. If stopping takes 175 | // too long, kill the whole process 176 | stop(false) 177 | if (getStatus((timeout getOrElse 10000.toLong) max 10000.toLong) == 178 | ProverStatus.Running) { 179 | println("(error \"timeout, killing solver process\")") 180 | System exit 1 181 | } 182 | } 183 | } 184 | } catch { 185 | case t@(TimeoutException | StoppedException) => 186 | println("unknown") 187 | case t : Throwable => { 188 | println("(error \"" + t.getMessage + "\")") 189 | // t.printStackTrace 190 | } 191 | } 192 | 193 | def main(args: Array[String]) : Unit = { 194 | doMain(args, false) 195 | } 196 | 197 | } 198 | -------------------------------------------------------------------------------- /src/main/scala/SMTReader.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sloth, an SMT solver for strings. 3 | * Copyright (C) 2017 Philipp Ruemmer, Petr Janku 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package strsolver 20 | 21 | import ap.parameters.{Param, ParserSettings} 22 | import ap.parser._ 23 | 24 | object SMTReader { 25 | import IExpression._ 26 | 27 | def eliminateUniQuantifiers(formula : IFormula) = { 28 | var clause = formula 29 | var parameterConsts = List[ITerm]() 30 | 31 | // transformation to prenex normal form 32 | clause = Transform2Prenex(Transform2NNF(clause)) 33 | 34 | while (clause.isInstanceOf[IQuantified]) { 35 | val IQuantified(Quantifier.ALL, d) = clause 36 | clause = d 37 | parameterConsts = 38 | (new ConstantTerm ("P" + parameterConsts.size)) :: parameterConsts 39 | } 40 | 41 | subst(clause, parameterConsts, 0) 42 | } 43 | } 44 | 45 | /** This class is borrowed from Eldarica, 46 | https://github.com/uuverifiers/eldarica */ 47 | class SMTReader(fileName: String) { 48 | import SMTReader._ 49 | 50 | private val reader = new java.io.BufferedReader ( 51 | new java.io.FileReader(new java.io.File (fileName))) 52 | private val settings = Param.BOOLEAN_FUNCTIONS_AS_PREDICATES.set( 53 | ParserSettings.DEFAULT, true) 54 | private val parser = SMTLIBStringParser(settings) 55 | val (rawFormula, _, signature) = parser(reader) 56 | reader.close 57 | // println(signature.order) 58 | 59 | val includesGetModel = parser.includesGetModel 60 | 61 | private val allConstants = parser.env.nullaryFunctions.toSeq sortBy (_.name) 62 | 63 | val (wordConstants, otherConstants) = 64 | allConstants partition { c => 65 | parser.env.lookupSym(c.name) match { 66 | case Environment.Constant(_, _, 67 | SMTLIBStringParser.SMTSeq(_)) => true 68 | case _ => false 69 | }} 70 | 71 | val bitwidth = parser.observedBitwidth getOrElse 8 72 | Console.err.println("Assuming bit-vectors of width " + bitwidth) 73 | 74 | def elimEqv(aF : ap.parser.IFormula) : ap.parser.IFormula = { 75 | aF match { 76 | case f@IBinFormula(IBinJunctor.Eqv,f1,f2) => 77 | val ff1 = elimEqv(f1) 78 | val ff2 = elimEqv(f2) 79 | IBinFormula(IBinJunctor.And, 80 | IBinFormula(IBinJunctor.Or,INot(ff1),ff2), 81 | IBinFormula(IBinJunctor.Or,INot(ff2),ff1)) 82 | case f => f 83 | } 84 | } 85 | def nnf(aF : ap.parser.IFormula, b : Boolean) : ap.parser.IFormula = { 86 | aF match { 87 | case INot(f) => 88 | nnf(f,!b) 89 | case IQuantified(q,f) => 90 | val qq = if (!b) q else q.dual 91 | IQuantified(qq,nnf(f,b)) 92 | case IBinFormula(j,f1,f2) => 93 | j match { 94 | case IBinJunctor.And => 95 | val jj = if (!b) IBinJunctor.And else IBinJunctor.Or 96 | IBinFormula(jj,nnf(f1,b),nnf(f2,b)) 97 | case IBinJunctor.Or => 98 | val jj = if (!b) IBinJunctor.Or else IBinJunctor.And 99 | IBinFormula(jj,nnf(f1,b),nnf(f2,b)) 100 | case IBinJunctor.Eqv => 101 | assert(false) 102 | IBoolLit(true) 103 | } 104 | case f : IAtom => if (!b) f else INot(f) 105 | case f : IBoolLit => if (!b) f else INot(f) 106 | case f : IIntFormula => if (!b) f else INot(f) 107 | case _ => 108 | assert(false) 109 | IBoolLit(true) 110 | } 111 | } 112 | // negation normal form 113 | def nnf(aF : ap.parser.IFormula) : ap.parser.IFormula = { 114 | val f_noEqv = elimEqv(aF) 115 | nnf(f_noEqv,false) 116 | } 117 | 118 | def dnf_base(dnf1 : List[ap.parser.IFormula], dnf2 : List[ap.parser.IFormula]) = { 119 | var dnf : List[ap.parser.IFormula] = List() 120 | for (f1 <- dnf1) { 121 | for (f2 <- dnf2) { 122 | dnf = (f1 &&& f2) :: dnf 123 | } 124 | } 125 | dnf 126 | } 127 | def cnf_base(cnf1 : List[ap.parser.IFormula], cnf2 : List[ap.parser.IFormula]) = { 128 | var cnf : List[ap.parser.IFormula] = List() 129 | for (f1 <- cnf1) { 130 | for (f2 <- cnf2) { 131 | cnf = (f1 ||| f2) :: cnf 132 | } 133 | } 134 | cnf 135 | } 136 | // disjunctive normal form (quantified subformulas are considered as atoms) 137 | def ddnf(aF : ap.parser.IFormula) : List[ap.parser.IFormula] = { 138 | var dnf : List[IFormula] = Nil 139 | aF match { 140 | case IBinFormula(j,f1,f2) => 141 | val dnf1 = ddnf(f1) 142 | val dnf2 = ddnf(f2) 143 | j match { 144 | case IBinJunctor.And => 145 | dnf = dnf_base(dnf1,dnf2) 146 | case IBinJunctor.Or => 147 | dnf = dnf1 ::: dnf2 148 | case IBinJunctor.Eqv => 149 | assert(false) 150 | } 151 | case f@INot(IAtom(_,_)) => dnf = f :: Nil 152 | case f@INot(IBoolLit(_)) => dnf = f :: Nil 153 | case f@INot(IIntFormula(_,_)) => dnf = f :: Nil 154 | case f : IAtom => dnf = f :: Nil 155 | case f : IBoolLit => dnf = f :: Nil 156 | case f : IIntFormula => dnf = f :: Nil 157 | case f : IQuantified => dnf = f :: Nil 158 | case _ => 159 | assert(false) 160 | } 161 | dnf 162 | } 163 | // conjunctive normal form (quantified subformulas are considered as atoms) 164 | def ccnf(aF : ap.parser.IFormula) : List[ap.parser.IFormula] = { 165 | var cnf : List[IFormula] = Nil 166 | aF match { 167 | case IBinFormula(j,f1,f2) => 168 | val cnf1 = ccnf(f1) 169 | val cnf2 = ccnf(f2) 170 | j match { 171 | case IBinJunctor.And => 172 | cnf = cnf1 ::: cnf2 173 | case IBinJunctor.Or => 174 | cnf = cnf_base(cnf1,cnf2) 175 | case IBinJunctor.Eqv => 176 | assert(false) 177 | } 178 | case f@INot(IAtom(_,_)) => cnf = f :: Nil 179 | case f@INot(IBoolLit(_)) => cnf = f :: Nil 180 | case f@INot(IIntFormula(_,_)) => cnf = f :: Nil 181 | case f : IAtom => cnf = f :: Nil 182 | case f : IBoolLit => cnf = f :: Nil 183 | case f : IIntFormula => cnf = f :: Nil 184 | case f : IQuantified => cnf = f :: Nil 185 | case _ => 186 | assert(false) 187 | } 188 | cnf 189 | } 190 | def containsPredicate(aF : IFormula) : Boolean = { 191 | aF match { 192 | case IQuantified(q,f) => containsPredicate(f) 193 | case IBinFormula(j,f1,f2) => containsPredicate(f1) || containsPredicate(f2) 194 | case INot(f) => containsPredicate(f) 195 | case a : IAtom => true 196 | case _ =>false 197 | } 198 | } 199 | def quantifiers(aF : IFormula) : List[ap.terfor.conjunctions.Quantifier] = { 200 | aF match { 201 | case IQuantified(q,f) => 202 | q :: quantifiers(f) 203 | case IBinFormula(j,f1,f2) => 204 | quantifiers(f1) ::: quantifiers(f2) 205 | case INot(f) => 206 | quantifiers(f) 207 | case _ => Nil 208 | } 209 | } 210 | def cnt_quantif(aF : IFormula) : Int = { 211 | quantifiers(aF).length 212 | } 213 | // prenex normal form 214 | // aF -- a formula formed only by atoms, quantifiers, not, and, or, eqv 215 | def pnf(aF : ap.parser.IFormula) : ap.parser.IFormula = { 216 | val f_nnf = nnf(aF) 217 | var x = cnt_quantif(f_nnf) 218 | // quantifier prefix of PNF 219 | var q_prefix = List[IExpression.Quantifier]() 220 | // aInx -- de Bruijn index 221 | def remove_quant(aF : IFormula, aInx : Int) : IFormula = { 222 | aF match { 223 | case IQuantified(q,f) => 224 | val ff = remove_quant(f,aInx+1) 225 | q_prefix = q :: q_prefix 226 | x = x-1 227 | // _aInx becomes _x 228 | // for each i in 0..(aInx-1), _i stays _i 229 | var ll = List[IVariable]() 230 | ll = IExpression.v(x) :: ll 231 | for (i <- aInx-1 to 0 by -1) { 232 | ll = IExpression.v(i) :: ll 233 | } 234 | val aux = VariableSubstVisitor(ff,(ll,0)) 235 | aux 236 | case IBinFormula(j,f1,f2) => 237 | val ff1 = remove_quant(f1,aInx) 238 | val ff2 = remove_quant(f2,aInx) 239 | IBinFormula(j,ff1,ff2) 240 | case INot(f) => 241 | val ff = remove_quant(f,aInx) 242 | INot(ff) 243 | case f => f 244 | } 245 | } 246 | // new de Bruijn indices: 0,1,...,x-1 247 | // renaming of variables in post-order traversal, starting with x-1 248 | var f_pnf = remove_quant(f_nnf,0) 249 | // add the quantifier prefix 250 | // quantifiers (q1,q2,...) in q_prefix have de Bruijn indices (0,1,...) 251 | for (q <- q_prefix.reverse) { 252 | f_pnf = IQuantified(q,f_pnf) 253 | } 254 | f_pnf 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /src/main/scala/Replace.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sloth, an SMT solver for strings. 3 | * Copyright (C) 2017 Philipp Ruemmer, Petr Janku 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package strsolver 20 | 21 | import scala.collection.JavaConverters._ 22 | import dk.brics.automaton.{Automaton, RegExp, SpecialOperations, State} 23 | 24 | import scala.collection.mutable.ArrayBuffer 25 | 26 | object Replace { 27 | private var var1 = 0 28 | private var var2 = 1 29 | 30 | private def buildAutomaton(pattern: String): Automaton = { 31 | val aut: Automaton = new RegExp(pattern).toAutomaton(true) 32 | 33 | aut.determinize() 34 | aut.minimize() 35 | aut 36 | } 37 | 38 | private def splitTransitions[T](list: List[(T, Int)]) = { 39 | val orderedList = list.sortWith {case (e1, e2) => e1._2 < e2._2} 40 | var tuple: Set[T] = Set(orderedList.head._1) 41 | var start: Int = orderedList.head._2 42 | var res: List[(Set[T], Range)] = List.empty 43 | 44 | def addElement(states: Set[T], range: Range): Unit = { 45 | if(res.nonEmpty && res.head._2 == range) 46 | res = (states ++ res.head._1, range) :: res.tail 47 | else if(res.nonEmpty && 48 | res.head._2.last == range.head && 49 | (res.head._1 subsetOf states)) 50 | res = (states, range) :: res.tail 51 | else if(res.nonEmpty && 52 | res.head._2.last == range.head && 53 | (states subsetOf res.head._1)) 54 | res = res 55 | else if(res.nonEmpty && 56 | res.head._2.last == range.head && 57 | res.head._2.length == 1) 58 | res = (states, range.head + 1 to range.last) :: 59 | (res.head._1 ++ states, res.head._2) :: 60 | res.tail 61 | else if (res.nonEmpty && 62 | res.head._2.last == range.head && 63 | res.head._2.length > 1) 64 | res = (states ++ res.head._1, range) :: 65 | (res.head._1, res.head._2.head until res.head._2.last) :: 66 | res.tail 67 | else 68 | res = (states, range) :: res 69 | } 70 | 71 | for(e <- orderedList.tail) { 72 | if(tuple.isEmpty) { 73 | tuple += e._1 74 | start = e._2 75 | } 76 | else if(tuple(e._1)) { 77 | addElement(tuple, 78 | if(start >= e._2) e._2 to e._2 else start to e._2) 79 | 80 | tuple -= e._1 81 | start = e._2 + 1 82 | } 83 | else { 84 | addElement(tuple, 85 | if(start >= e._2) e._2 to e._2 else start until e._2) 86 | tuple += e._1 87 | start = e._2 88 | } 89 | } 90 | 91 | res 92 | } 93 | 94 | private def createSymbolFromRange(r: Range): AFormula = 95 | if(r.head == r.last) 96 | AFormula.createSymbol(r.head, var1) 97 | else 98 | AFormula.symbolInRange(r.head, r.last, var1) 99 | 100 | private def buildSymbolIdentity(v1: Int, v2: Int): AFormula = 101 | (0 until AFormula.widthOfChar).foldLeft[AFormula](AFTrue) { 102 | case (f, bit) => 103 | f & (AFCharVar(bit + AFormula.widthOfChar * v1) <=> 104 | AFCharVar(bit + AFormula.widthOfChar * v2)) 105 | } 106 | 107 | private def buildBeginMarker(aut: Automaton): AFA = { 108 | val initSet = Set(aut.getInitialState) 109 | var cache: Map[State, List[(State, Int)]] = Map.empty 110 | var workList: List[Set[State]] = List(Set(aut.getInitialState)) 111 | var transitions: Map[Set[State], Map[Set[State], AFormula]] = 112 | Map(initSet -> Map.empty[Set[State], AFormula]) 113 | def getTransitions(states: Set[State]) = { 114 | var res: List[(State, Int)] = List.empty 115 | 116 | for(s <- states) 117 | res = (cache get s match { 118 | case Some(x) => x 119 | case None => 120 | var res: List[(State, Int)] = List.empty 121 | 122 | for(t <- s.getTransitions.asScala) 123 | res = (t.getDest, t.getMin.toInt) :: 124 | (t.getDest, t.getMax.toInt) :: res 125 | 126 | cache += s -> res 127 | res 128 | }) ::: res 129 | 130 | res 131 | } 132 | def buildAFA: AFA = { 133 | val symbolIdentity = buildSymbolIdentity(var1, var2) 134 | var setToState: Map[Set[State], Int] = Map.empty 135 | val states: ArrayBuffer[AFormula] = 136 | ArrayBuffer.fill[AFormula](transitions.size)(AFFalse) 137 | 138 | transitions foreach(e => setToState += e._1 -> (setToState.size + 1)) 139 | transitions foreach { case(s, t) => 140 | val formula = t.foldLeft[AFormula](AFFalse) { case (f, e) => 141 | f | (AFStateVar(setToState(e._1)) & 142 | ~AFSpecSymb(var1) & ~AFSpecSymb(var2) & symbolIdentity & e._2) 143 | } 144 | 145 | states(setToState(s) - 1) = 146 | if (s forall (!_.isAccept)) { 147 | formula 148 | } 149 | else { 150 | AFFalse 151 | } 152 | } 153 | 154 | val initState = (1 to transitions.size).foldLeft[AFormula](AFFalse) { 155 | _ | AFStateVar(_) } & 156 | AFormula.createEpsilon(var1) & 157 | AFormula.createEpsilon(var2) 158 | 159 | val resStates = initState +: states.toVector 160 | val finalFormula = ((0 until resStates.size).toSet - 161 | setToState(Set(aut.getInitialState))).foldLeft[AFormula](AFTrue) ( 162 | _ & ~AFStateVar(_) 163 | ) 164 | 165 | new AFA(AFStateVar(0), 166 | resStates, 167 | finalFormula) 168 | } 169 | 170 | while(workList.nonEmpty) { 171 | val tuple = workList.head 172 | val res = splitTransitions(getTransitions(tuple + aut.getInitialState)) 173 | var neg: AFormula = AFFalse 174 | var upperBound: Int = 0xffff // max. value of unicode. 175 | def update(map: Map[Set[State], AFormula], f: AFormula) = 176 | map + (tuple -> (map.getOrElse(tuple, AFFalse) | f)) 177 | 178 | workList = workList.tail 179 | res foreach { e => 180 | if(e._2.last != upperBound) 181 | neg |= createSymbolFromRange((e._2.last + 1) to upperBound) 182 | 183 | upperBound = e._2.head - 1 184 | transitions += e._1 -> (transitions get e._1 match { 185 | case Some(x) => 186 | update(x, createSymbolFromRange(e._2)) 187 | 188 | case None => 189 | workList = e._1 :: workList 190 | Map(tuple -> createSymbolFromRange(e._2)) 191 | })} 192 | 193 | if(upperBound > 0) neg |= createSymbolFromRange(0 to upperBound) 194 | transitions += initSet -> update(transitions(initSet), neg) 195 | } 196 | 197 | buildAFA 198 | } 199 | 200 | private def brics2AFATransducer(aut : Automaton, 201 | v: Int, 202 | ev: Int) : AFA = { 203 | var finalStates: AFormula = AFFalse 204 | val initState = aut.getInitialState 205 | val states = initState +: (aut.getStates.asScala - initState).toVector 206 | val stateInd = states.iterator.zipWithIndex.toMap 207 | val partOfLabel = ~AFSpecSymb(v) & AFormula.createEpsilon(ev) 208 | val transFors: Vector[AFormula] = states map { state => 209 | if(state.isAccept) 210 | finalStates |= AFStateVar(stateInd(state)) 211 | 212 | state.getTransitions.asScala.foldLeft[AFormula](AFFalse) { 213 | case (f, t) => 214 | val targetConstr = AFStateVar(stateInd(t.getDest)) 215 | val labelConstr = 216 | if (t.getMin == t.getMax) 217 | partOfLabel & AFormula.createSymbol(t.getMin, v) 218 | else 219 | partOfLabel & AFormula.symbolInRange(t.getMin, t.getMax, v) 220 | 221 | f | (targetConstr & labelConstr) 222 | } 223 | } 224 | 225 | new AFA(AFStateVar(0), transFors, finalStates) 226 | } 227 | 228 | private def concatenation(lAfa: AFA, rAfa: AFA): AFA = { 229 | val reAfa: AFA = rAfa shiftStateVariables lAfa.states.size 230 | val rAfaIS: AFormula = reAfa.states.head 231 | var states: Vector[AFormula] = lAfa.states ++: reAfa.states 232 | val finalStateSet = 233 | if(rAfa.finalStateSet(0)) 234 | lAfa.finalStateSet ++ reAfa.finalStateSet 235 | else 236 | reAfa.finalStateSet 237 | val finalStates: AFormula = 238 | (states.indices.toSet -- finalStateSet).foldLeft[AFormula](AFTrue) ( 239 | _ & ~AFStateVar(_) 240 | ) 241 | 242 | lAfa.finalStateSet.foreach(s => states = states.updated(s, rAfaIS | states(s))) 243 | new AFA(AFStateVar(0), states, finalStates) 244 | } 245 | 246 | private def product(aut1: Automaton, aut2: Automaton): AFA = { 247 | val afa1: AFA = brics2AFATransducer(aut1, var1, var2) 248 | val afa2: AFA = brics2AFATransducer(aut2, var2, var1) 249 | val states = (afa1.states.view.zipWithIndex map {case (transitions, state) => 250 | if(afa1.finalStateSet contains state) 251 | AFFalse 252 | else 253 | transitions 254 | }).toVector 255 | 256 | concatenation(new AFA(AFStateVar(0), states, afa1.finalStates), afa2) 257 | } 258 | 259 | private def buildBeginMarker(pattern: String): AFA = { 260 | val aut: Automaton = buildAutomaton(pattern) 261 | 262 | SpecialOperations.reverse(aut) 263 | aut.determinize() 264 | aut.minimize() 265 | 266 | buildBeginMarker(aut) 267 | } 268 | 269 | private def buildReplaceAut(pattern: Automaton, 270 | substitution: Automaton): AFA = { 271 | pattern.getAcceptStates.asScala.foreach(_.getTransitions.clear()) 272 | pattern.determinize() 273 | pattern.minimize() 274 | 275 | val symbolIdentity = buildSymbolIdentity(var1, var2) 276 | val prdct = product(pattern, substitution) 277 | var states: Vector[AFormula] = Vector.empty 278 | prdct.states.zipWithIndex foreach { case (state, ind) => 279 | states :+= ( 280 | if(state == AFFalse) 281 | AFStateVar(ind) & ~AFSpecSymb(var1) & ~AFSpecSymb(var2) & symbolIdentity 282 | else state) 283 | } 284 | 285 | new AFA(prdct.initialStates, states, prdct.finalStates) 286 | } 287 | 288 | def apply(pattern: String, substitution: String): AFA = { 289 | val autPattern: Automaton = buildAutomaton(pattern) 290 | val autSubstit: Automaton = buildAutomaton(substitution) 291 | val bm = buildBeginMarker(pattern) 292 | 293 | val repl = 294 | buildReplaceAut(autPattern, 295 | autSubstit) shiftStateVariables bm.states.size - 1 296 | var states: Vector[AFormula] = Vector.empty 297 | 298 | bm.states foreach { state => 299 | states :+= (if(state == AFFalse) repl.states.head else state) 300 | } 301 | 302 | states ++= repl.states.tail 303 | val finalFormula = repl.finalStates & bm.finalStates 304 | 305 | new AFA(bm.initialStates, states, finalFormula) 306 | } 307 | 308 | def apply(pattern: String, substitution: String, v1: Int, v2: Int): AFA = { 309 | var1 = v1 310 | var2 = v2 311 | apply(pattern, substitution) 312 | } 313 | } 314 | -------------------------------------------------------------------------------- /src/main/scala/ReplaceAll.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sloth, an SMT solver for strings. 3 | * Copyright (C) 2017 Philipp Ruemmer, Petr Janku 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package strsolver 20 | 21 | import scala.collection.JavaConverters._ 22 | import dk.brics.automaton.{Automaton, RegExp, SpecialOperations, State} 23 | 24 | import scala.collection.mutable.ArrayBuffer 25 | 26 | object ReplaceAll { 27 | private var var1 = 0 28 | private var var2 = 1 29 | private var var3 = 2 30 | 31 | private def buildAutomaton(pattern: String): Automaton = { 32 | val aut: Automaton = new RegExp(pattern).toAutomaton(true) 33 | 34 | aut.determinize() 35 | aut.minimize() 36 | aut 37 | } 38 | 39 | private def splitTransitions[T](list: List[(T, Int)]) = { 40 | val orderedList = list.sortWith {case (e1, e2) => e1._2 < e2._2} 41 | var tuple: Set[T] = Set(orderedList.head._1) 42 | var start: Int = orderedList.head._2 43 | var res: List[(Set[T], Range)] = List.empty 44 | 45 | def addElement(states: Set[T], range: Range): Unit = { 46 | if(res.nonEmpty && res.head._2 == range) 47 | res = (states ++ res.head._1, range) :: res.tail 48 | else if(res.nonEmpty && 49 | res.head._2.last == range.head && 50 | (res.head._1 subsetOf states)) 51 | res = (states, range) :: res.tail 52 | else if(res.nonEmpty && 53 | res.head._2.last == range.head && 54 | (states subsetOf res.head._1)) 55 | res = res 56 | else if(res.nonEmpty && 57 | res.head._2.last == range.head && 58 | res.head._2.length == 1) 59 | res = (states, range.head + 1 to range.last) :: 60 | (res.head._1 ++ states, res.head._2) :: 61 | res.tail 62 | else if (res.nonEmpty && 63 | res.head._2.last == range.head && 64 | res.head._2.length > 1) 65 | res = (states ++ res.head._1, range) :: 66 | (res.head._1, res.head._2.head until res.head._2.last) :: 67 | res.tail 68 | else 69 | res = (states, range) :: res 70 | } 71 | 72 | for(e <- orderedList.tail) { 73 | if(tuple.isEmpty) { 74 | tuple += e._1 75 | start = e._2 76 | } 77 | else if(tuple(e._1)) { 78 | addElement(tuple, 79 | if(start >= e._2) e._2 to e._2 else start to e._2) 80 | 81 | tuple -= e._1 82 | start = e._2 + 1 83 | } 84 | else { 85 | addElement(tuple, 86 | if(start >= e._2) e._2 to e._2 else start until e._2) 87 | tuple += e._1 88 | start = e._2 89 | } 90 | } 91 | 92 | res 93 | } 94 | 95 | private def createSymbolFromRange(r: Range): AFormula = 96 | if(r.head == r.last) 97 | AFormula.createSymbol(r.head, var1) 98 | else 99 | AFormula.symbolInRange(r.head, r.last, var1) 100 | 101 | private def buildSymbolIdentity(v1: Int, v2: Int): AFormula = 102 | (0 until AFormula.widthOfChar).foldLeft[AFormula](AFTrue) { 103 | case (f, bit) => 104 | f & (AFCharVar(bit + AFormula.widthOfChar * v1) <=> 105 | AFCharVar(bit + AFormula.widthOfChar * v2)) 106 | } 107 | 108 | private def buildBeginMarker(aut: Automaton): AFA = { 109 | val initSet = Set(aut.getInitialState) 110 | var cache: Map[State, List[(State, Int)]] = Map.empty 111 | var workList: List[Set[State]] = List(Set(aut.getInitialState)) 112 | var transitions: Map[Set[State], Map[Set[State], AFormula]] = 113 | Map(initSet -> Map.empty[Set[State], AFormula]) 114 | def getTransitions(states: Set[State]) = { 115 | var res: List[(State, Int)] = List.empty 116 | 117 | for(s <- states) 118 | res = (cache get s match { 119 | case Some(x) => x 120 | case None => 121 | var res: List[(State, Int)] = List.empty 122 | 123 | for(t <- s.getTransitions.asScala) 124 | res = (t.getDest, t.getMin.toInt) :: 125 | (t.getDest, t.getMax.toInt) :: res 126 | 127 | cache += s -> res 128 | res 129 | }) ::: res 130 | 131 | res 132 | } 133 | def buildAFA: AFA = { 134 | val symbolIdentity = buildSymbolIdentity(var1, var2) 135 | var setToState: Map[Set[State], Int] = Map.empty 136 | val states: ArrayBuffer[AFormula] = 137 | ArrayBuffer.fill[AFormula](transitions.size)(AFFalse) 138 | 139 | transitions foreach(e => setToState += e._1 -> (setToState.size + 1)) 140 | transitions foreach { case(s, t) => 141 | val formula = t.foldLeft[AFormula](AFFalse) { case (f, e) => 142 | f | (AFStateVar(setToState(e._1)) & 143 | ~AFSpecSymb(var1) & ~AFSpecSymb(var2) & symbolIdentity & e._2) 144 | } 145 | 146 | states(setToState(s) - 1) = 147 | if (s forall (!_.isAccept)) { 148 | formula 149 | } 150 | else { 151 | states += formula 152 | AFStateVar(states.size) & 153 | AFormula.createEpsilon(var1) & 154 | AFormula.createSpecialSymbol(1, var2) 155 | } 156 | } 157 | 158 | val initState = (1 to transitions.size).foldLeft[AFormula](AFFalse) { 159 | _ | AFStateVar(_) } & 160 | AFormula.createEpsilon(var1) & 161 | AFormula.createEpsilon(var2) 162 | 163 | val resStates = initState +: states.toVector 164 | val finalFormula = ((0 until resStates.size).toSet - 165 | setToState(Set(aut.getInitialState))).foldLeft[AFormula](AFTrue) ( 166 | _ & ~AFStateVar(_) 167 | ) 168 | 169 | new AFA(AFStateVar(0), 170 | resStates, 171 | finalFormula) 172 | } 173 | 174 | while(workList.nonEmpty) { 175 | val tuple = workList.head 176 | val res = splitTransitions(getTransitions(tuple + aut.getInitialState)) 177 | var neg: AFormula = AFFalse 178 | var upperBound: Int = 0xffff // max. value of unicode. 179 | def update(map: Map[Set[State], AFormula], f: AFormula) = 180 | map + (tuple -> (map.getOrElse(tuple, AFFalse) | f)) 181 | 182 | workList = workList.tail 183 | res foreach { e => 184 | if(e._2.last != upperBound) 185 | neg |= createSymbolFromRange((e._2.last + 1) to upperBound) 186 | 187 | upperBound = e._2.head - 1 188 | transitions += e._1 -> (transitions get e._1 match { 189 | case Some(x) => 190 | update(x, createSymbolFromRange(e._2)) 191 | 192 | case None => 193 | workList = e._1 :: workList 194 | Map(tuple -> createSymbolFromRange(e._2)) 195 | })} 196 | 197 | if(upperBound > 0 ) neg |= createSymbolFromRange(0 to upperBound) 198 | transitions += initSet -> update(transitions(initSet), neg) 199 | } 200 | 201 | buildAFA 202 | } 203 | 204 | private def brics2AFATransducer(aut : Automaton, 205 | v: Int, 206 | ev: Int) : AFA = { 207 | var finalStates: AFormula = AFFalse 208 | val initState = aut.getInitialState 209 | val states = initState +: (aut.getStates.asScala - initState).toVector 210 | val stateInd = states.iterator.zipWithIndex.toMap 211 | val partOfLabel = ~AFSpecSymb(v) & AFormula.createEpsilon(ev) 212 | val transFors: Vector[AFormula] = states map { state => 213 | if(state.isAccept) 214 | finalStates |= AFStateVar(stateInd(state)) 215 | 216 | state.getTransitions.asScala.foldLeft[AFormula](AFFalse) { 217 | case (f, t) => 218 | val targetConstr = AFStateVar(stateInd(t.getDest)) 219 | val labelConstr = 220 | if (t.getMin == t.getMax) 221 | partOfLabel & AFormula.createSymbol(t.getMin, v) 222 | else 223 | partOfLabel & AFormula.symbolInRange(t.getMin, t.getMax, v) 224 | 225 | f | (targetConstr & labelConstr) 226 | } 227 | } 228 | 229 | new AFA(AFStateVar(0), transFors, finalStates) 230 | } 231 | 232 | private def concatenation(lAfa: AFA, rAfa: AFA): AFA = { 233 | val reAfa: AFA = rAfa shiftStateVariables lAfa.states.size 234 | val rAfaIS: AFormula = reAfa.states.head 235 | var states: Vector[AFormula] = lAfa.states ++: reAfa.states 236 | val finalStates: AFormula = 237 | if(rAfa.finalStateSet(0)) 238 | lAfa.finalStates | reAfa.finalStates 239 | else 240 | reAfa.finalStates 241 | 242 | lAfa.finalStateSet.foreach(s => states = states.updated(s, rAfaIS | states(s))) 243 | new AFA(AFStateVar(0), states, finalStates) 244 | } 245 | 246 | private def product(aut1: Automaton, aut2: Automaton): AFA = { 247 | val afa1: AFA = brics2AFATransducer(aut1, var2, var3) 248 | val afa2: AFA = brics2AFATransducer(aut2, var3, var2) 249 | val symbol = AFormula.createSpecialSymbol(1, var2) & AFormula.createEpsilon(var3) 250 | val states = (afa1.states.view.zipWithIndex map {case (transitions, state) => 251 | if(afa1.finalStateSet contains state) 252 | AFFalse 253 | else 254 | transitions | (AFStateVar(state) & symbol) 255 | }).toVector 256 | 257 | concatenation(new AFA(AFStateVar(0), states, afa1.finalStates), afa2) 258 | } 259 | 260 | private def A4ReplaceAllReluctant(pattern: Automaton, substit: Automaton): AFA = { 261 | def createSymbolFormula(range: Range, v: Int): AFormula = 262 | range.foldLeft[AFormula](AFFalse)(_ | AFormula.createSymbol(_, v)) 263 | 264 | pattern.getAcceptStates.asScala.foreach(_.getTransitions.clear()) 265 | pattern.determinize() 266 | pattern.minimize() 267 | val afa: AFA = product(pattern, substit) shiftStateVariables 1 268 | val initTrans = 269 | (AFStateVar(0) & ~AFSpecSymb(var2) & ~AFSpecSymb(var3) & buildSymbolIdentity(var2, var3)) | 270 | (AFStateVar(1) & AFormula.createSpecialSymbol(1, var2) & AFormula.createEpsilon(var3)) 271 | var states: Vector[AFormula] = initTrans +: afa.states 272 | val symbol: AFormula = AFormula.createEpsilon(var2) & AFormula.createEpsilon(var3) 273 | val finalFormula = (1 until states.size).foldLeft[AFormula](AFTrue) ( 274 | _ & ~AFStateVar(_) 275 | ) 276 | 277 | afa.finalStateSet.foreach { state => 278 | states = states.updated(state, AFormula(Set(0)) & symbol) 279 | } 280 | 281 | new AFA(AFStateVar(0), states, finalFormula) 282 | } 283 | 284 | private def buildBeginMarker(pattern: String): AFA = { 285 | val aut: Automaton = buildAutomaton(pattern) 286 | 287 | SpecialOperations.reverse(aut) 288 | aut.determinize() 289 | aut.minimize() 290 | 291 | buildBeginMarker(aut) 292 | } 293 | 294 | def apply(pattern: String, substitution: String): AFA = { 295 | val autPattern: Automaton = buildAutomaton(pattern) 296 | val autSubstitution: Automaton = buildAutomaton(substitution) 297 | AFA.synchronise(buildBeginMarker(pattern), 298 | A4ReplaceAllReluctant(autPattern, autSubstitution), var2) 299 | } 300 | 301 | def getAFAs(pattern: String, substitution: String): (AFA, AFA) = { 302 | val autPattern: Automaton = buildAutomaton(pattern) 303 | val autSubstitution: Automaton = buildAutomaton(substitution) 304 | var1 = 1 305 | var2 = 0 306 | val res1 = buildBeginMarker(pattern) 307 | var2 = 1 308 | var3 = 0 309 | val res2 = A4ReplaceAllReluctant(autPattern, autSubstitution) 310 | 311 | (res1, res2) 312 | } 313 | 314 | def apply(pattern: String, substitution: String, v1: Int, v2: Int): AFA = { 315 | var1 = v1 316 | var3 = v2 317 | apply(pattern, substitution) 318 | } 319 | } 320 | -------------------------------------------------------------------------------- /src/main/scala/Emptiness.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sloth, an SMT solver for strings. 3 | * Copyright (C) 2017 Philipp Ruemmer, Petr Janku 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package strsolver 20 | 21 | import java.io.{BufferedReader, File, FileOutputStream, InputStreamReader, PrintWriter} 22 | import scala.collection.mutable.{ArrayBuffer, BitSet => MBitSet} 23 | 24 | object IsEmpty { 25 | def apply(afa : AFA) : Boolean = FindAcceptedWords(afa).isEmpty 26 | } 27 | 28 | object FindAcceptedWords { 29 | def apply(afa : AFA) : Option[Vector[Seq[Int]]] = { 30 | val normAFA = afa.eliminateParameters.normaliseInitial 31 | val aagFile = File.createTempFile("afa", ".aag") 32 | // println("Writing AIGER to " + aagFile) 33 | 34 | val out = new FileOutputStream(aagFile) 35 | Console.withOut(out) { 36 | new AFA2AAG(normAFA) 37 | } 38 | out.close 39 | 40 | val res = 41 | if(Flags.isABC) 42 | abcChecker(aagFile) 43 | else 44 | nuXmvChecker(aagFile) 45 | 46 | aagFile.delete 47 | 48 | res 49 | } 50 | 51 | private val NUXMV_CMD = "nuXmv" 52 | //private val NUXMV_CMD = "nth/touXmv" 53 | private val NUXMV_INPUT = "read_aiger_model; go_bmc; check_invar_ic3 ; quit" 54 | 55 | private val NUXMV_EMPTY_STR = """.*invariant +!NonAccepting +is +true""".r 56 | private val NUXMV_NON_EMPTY_STR = """.*invariant +!NonAccepting +is +false""".r 57 | private val NUXMV_INPUT_START = """.*-> Input: (.*) <-""".r 58 | private val NUXMV_INPUT_CHAR = """.*char ([0-9]*) = (.*)""".r 59 | private val NUXMV_INPUT_EPS = """.*eps ([0-9]*) = (.*)""".r 60 | 61 | private val ABC_NON_EMPTY_STR = """Output 0.*""".r 62 | private val ABC_EMPTY_STR = """Property proved.*""".r 63 | private val ABC_CMD = "abc" 64 | private val ABC_AIGTOAIG_CMD = "aigtoaig" 65 | 66 | private def abcChecker(aagFile : File) : Option[Vector[Seq[Int]]] = { 67 | val model = File.createTempFile("afa", ".aig") 68 | Runtime.getRuntime.exec(ABC_AIGTOAIG_CMD + " " + aagFile + " " + model) 69 | val p = Runtime.getRuntime.exec(ABC_CMD) 70 | val output = new BufferedReader(new InputStreamReader (p.getInputStream)) 71 | val input = new PrintWriter (p.getOutputStream) 72 | 73 | input.println("read_aiger " + model + "; pdr; quit") 74 | input.close 75 | 76 | var emptyRes = false 77 | var nonEmptyRes = false 78 | 79 | val charInputs, epsInputs = new ArrayBuffer[MBitSet] 80 | var maxCharIndex = -1 81 | 82 | var str = output.readLine 83 | while (str != null) { 84 | str match { 85 | case ABC_EMPTY_STR() => 86 | emptyRes = true 87 | case ABC_NON_EMPTY_STR() => 88 | nonEmptyRes = true 89 | case _ => // ignore 90 | } 91 | str = output.readLine 92 | } 93 | 94 | p.waitFor 95 | output.close 96 | model.delete 97 | 98 | assert(emptyRes != nonEmptyRes) 99 | 100 | if (nonEmptyRes) { 101 | Some(Vector.empty[Seq[Int]]) 102 | } else { 103 | None 104 | } 105 | } 106 | 107 | private def nuXmvChecker(aagFile : File) : Option[Vector[Seq[Int]]] = { 108 | val p = Runtime.getRuntime.exec(NUXMV_CMD + " -int " + aagFile) 109 | val output = new BufferedReader(new InputStreamReader (p.getInputStream)) 110 | val input = new PrintWriter (p.getOutputStream) 111 | 112 | input.println(NUXMV_INPUT) 113 | input.close 114 | 115 | var emptyRes = false 116 | var nonEmptyRes = false 117 | 118 | val charInputs, epsInputs = new ArrayBuffer[MBitSet] 119 | var maxCharIndex = -1 120 | 121 | var str = output.readLine 122 | while (str != null) { 123 | // println(str) 124 | str match { 125 | case NUXMV_EMPTY_STR() => 126 | emptyRes = true 127 | case NUXMV_NON_EMPTY_STR() => 128 | nonEmptyRes = true 129 | 130 | // read the counterexample 131 | case NUXMV_INPUT_START(num) => 132 | if (charInputs.isEmpty) { 133 | charInputs += new MBitSet 134 | epsInputs += new MBitSet 135 | } else { 136 | charInputs += charInputs.last.clone 137 | epsInputs += epsInputs.last.clone 138 | } 139 | case NUXMV_INPUT_CHAR(num, "TRUE") => { 140 | charInputs.last += num.toInt 141 | maxCharIndex = maxCharIndex max num.toInt 142 | } 143 | case NUXMV_INPUT_CHAR(num, "FALSE") => { 144 | charInputs.last -= num.toInt 145 | maxCharIndex = maxCharIndex max num.toInt 146 | } 147 | case NUXMV_INPUT_EPS(num, "TRUE") => 148 | epsInputs.last += num.toInt 149 | case NUXMV_INPUT_EPS(num, "FALSE") => 150 | epsInputs.last -= num.toInt 151 | 152 | case _ => // ignore 153 | } 154 | str = output.readLine 155 | } 156 | 157 | p.waitFor 158 | output.close 159 | 160 | assert(emptyRes != nonEmptyRes) 161 | 162 | if (nonEmptyRes) { 163 | // reconstruct words 164 | val W = AFormula.widthOfChar 165 | val numWords = (maxCharIndex + 1) / W 166 | val words = Array.fill(numWords)(new ArrayBuffer[Int]) 167 | 168 | for ((chars, eps) <- charInputs.iterator zip epsInputs.iterator) 169 | for (n <- 0 until numWords) 170 | if (!(eps contains n)) { 171 | words(n) += 172 | (for (k <- 0 until W; if (chars contains (n * W + k))) 173 | yield (1 << (W - k - 1))).sum 174 | } 175 | 176 | Some(words.toVector) 177 | } else { 178 | None 179 | } 180 | } 181 | } 182 | 183 | //////////////////////////////////////////////////////////////////////////////// 184 | 185 | class AFA2AAG(afa : AFA) { 186 | val specSyms = afa.specSyms.toList.sorted 187 | val numInputs = afa.states.size + (afa.maxCharIndex + 1) + specSyms.size 188 | val numLatches = afa.states.size + 1 189 | 190 | type Variable = Int 191 | type Literal = (Variable, Boolean) 192 | 193 | private var nextVar = 1 194 | 195 | private def reserveVar : Variable = { 196 | val res = nextVar 197 | nextVar = nextVar + 1 198 | res 199 | } 200 | 201 | private def reserveVars(n : Int) : Vector[Variable] = { 202 | val res = (nextVar until (nextVar + n)).toVector 203 | nextVar = nextVar + n 204 | res 205 | } 206 | 207 | ////////////////////////////////////////////////////////////////////////////// 208 | 209 | val lines = new ArrayBuffer[String] 210 | 211 | // printing variables 212 | def p(ind : Variable) : String = "" + (ind*2) 213 | def n(ind : Variable) : String = "" + (ind*2 + 1) 214 | 215 | // printing literals 216 | def p(ind : Literal) : String = ind match { 217 | case (i, false) => p(i) 218 | case (i, true) => n(i) 219 | } 220 | 221 | def n(ind : Literal) : String = p((ind._1, !ind._2)) 222 | 223 | private def out(s : String*) = 224 | lines += (s mkString " ") 225 | 226 | ////////////////////////////////////////////////////////////////////////////// 227 | 228 | val nextStateInd = reserveVars(afa.states.size) 229 | val inputBitInd = reserveVars(afa.maxCharIndex + 1) 230 | val specSymInd = reserveVars(specSyms.size) 231 | val oldStateInd = reserveVars(afa.states.size) 232 | val noNewErrInd = reserveVar 233 | val oldErrInd = reserveVar 234 | val reachCheck = reserveVar 235 | 236 | private def encodeFor(f : AFormula, 237 | boundVars : List[Literal], 238 | stateInd : Vector[Variable]) : Literal = f match { 239 | case AFAnd(s1, s2) => { 240 | val r1 = encodeFor(s1, boundVars, stateInd) 241 | val r2 = encodeFor(s2, boundVars, stateInd) 242 | val r = reserveVar 243 | out(p(r), p(r1), p(r2)) 244 | (r, false) 245 | } 246 | case AFOr(s1, s2) => { 247 | val r1 = encodeFor(s1, boundVars, stateInd) 248 | val r2 = encodeFor(s2, boundVars, stateInd) 249 | val r = reserveVar 250 | out(p(r), n(r1), n(r2)) 251 | (r, true) 252 | } 253 | case AFLet(s1, s2) => { 254 | val r1 = encodeFor(s1, boundVars, stateInd) 255 | encodeFor(s2, r1 :: boundVars, stateInd) 256 | } 257 | case AFNot(s) => { 258 | val (ind, b) = encodeFor(s, boundVars, stateInd) 259 | (ind, !b) 260 | } 261 | case AFStateVar(v) => 262 | (stateInd(v), false) 263 | case AFCharVar(v) => 264 | (inputBitInd(v), false) 265 | case AFSpecSymb(ind) => 266 | (specSymInd(specSyms indexOf ind), false) 267 | case AFDeBrujinVar(ind) => 268 | boundVars(ind) 269 | case AFFalse => 270 | (0, false) 271 | case AFTrue => 272 | (0, true) 273 | } 274 | 275 | private def exactlyOne(vars: Set[Int]): Literal = { 276 | def and(lit1: String, lit2: String): Variable = { 277 | val res = reserveVar 278 | 279 | out(p(res), lit1, lit2) 280 | res 281 | } 282 | 283 | if(vars.size < 2) 284 | return (0, true) 285 | 286 | var tail = vars 287 | var res: Literal = 288 | vars.tail.foldLeft[Literal]((nextStateInd(vars.head), true)) { 289 | case (r, e) => (and(p(r), n(nextStateInd(e))), false) 290 | } 291 | 292 | res = (res._1, true) 293 | for(x <- vars.init) { 294 | tail = tail.tail 295 | for(y <- tail) { 296 | res = (and(p(res), n(and(p(nextStateInd(x)), p(nextStateInd(y))))), false) 297 | } 298 | } 299 | 300 | res 301 | } 302 | 303 | ////////////////////////////////////////////////////////////////////////////// 304 | 305 | // Print inputs 306 | 307 | // s' 308 | for (i <- nextStateInd) 309 | out(p(i)) 310 | 311 | // l 312 | for (i <- inputBitInd) 313 | out(p(i)) 314 | 315 | // specSyms 316 | for (i <- specSymInd) 317 | out(p(i)) 318 | 319 | // Print latches 320 | 321 | // s <- s' 322 | for (((o, n), num) <- 323 | (oldStateInd.iterator zip nextStateInd.iterator).zipWithIndex) 324 | out(p(o), p(n), if (num == 0) "1" else "0") 325 | 326 | out(p(oldErrInd), n(noNewErrInd), "0") 327 | 328 | // Print property 329 | out(p(reachCheck)) 330 | 331 | // Print transition formulas (AND gates) 332 | 333 | val andStartIndex = lines.size 334 | 335 | // T_i[s', l] 336 | val stateForInd = 337 | if(Flags.minimalSuccessors) { 338 | afa.states.zipWithIndex map { 339 | case (af, i) if i > 0 => 340 | val res = reserveVar 341 | 342 | val tmp = exactlyOne(af.getStates) 343 | out(p(res), p(encodeFor(af, List(), nextStateInd)), p(tmp)) 344 | (res, false) 345 | 346 | case (af, _) => encodeFor(af, List(), nextStateInd) 347 | } 348 | } else { 349 | afa.states map (encodeFor(_, List(), nextStateInd)) 350 | } 351 | 352 | // s_i -> T_i[s', l] (= !(s_i & !T_i[s', l])) 353 | val transitionConjuncts = 354 | for ((s, l) <- oldStateInd zip stateForInd) yield { 355 | val c = reserveVar 356 | out(p(c), p(s), n(l)) 357 | (c, true) 358 | } 359 | 360 | // /\_i s_i -> T_i[s', l] 361 | val transitionConjunction = 362 | transitionConjuncts reduceLeft[Literal] { 363 | case (c1, c2) => { 364 | val c = reserveVar 365 | out(p(c), p(c1), p(c2)) 366 | (c, false) 367 | } 368 | } 369 | 370 | // newErr <- oldErr | !transitionConjunction 371 | out(p(noNewErrInd), n(oldErrInd), p(transitionConjunction)) 372 | 373 | // encoded final formula of AFA 374 | val allAccepting = encodeFor(afa.finalStates, List(), oldStateInd) 375 | 376 | // Configuration signalling non-emptiness 377 | out(p(reachCheck), n(oldErrInd), p(allAccepting)) 378 | 379 | val numAnds = lines.size - andStartIndex 380 | 381 | ////////////////////////////////////////////////////////////////////////////// 382 | 383 | // Print actual AIGER output 384 | 385 | println("aag " + (nextVar - 1) + " " + 386 | numInputs + " " + numLatches + " 0 " + numAnds + " 1") 387 | lines map println 388 | 389 | // Add symbol table 390 | println("b0 NonAccepting") 391 | for (v <- 0 until inputBitInd.size) 392 | println("i" + (afa.states.size + v) + " " + "char " + v) 393 | for ((sym, num) <- specSyms.iterator.zipWithIndex) 394 | println("i" + (afa.states.size + (afa.maxCharIndex + 1) + num) 395 | + " " + "eps " + sym) 396 | } 397 | 398 | //////////////////////////////////////////////////////////////////////////////// 399 | 400 | object EmptinessMain extends App { 401 | println("Test AFA:") 402 | 403 | val s = AFStateVar(0) 404 | val q0 = AFStateVar(1) 405 | val q1 = AFStateVar(2) 406 | val q2 = AFStateVar(3) 407 | 408 | val a = AFormula.createSymbol(0, 0) 409 | val b = AFormula.createSymbol(1, 0) 410 | 411 | val postS = (a & s) | (b & s & q0) 412 | val postQ0 = (a & q1) | (b & (q0 | q1)) 413 | val postQ1 = (a & q0) | (b & (q1 | q2)) 414 | val postQ2 = a & q2 415 | 416 | val finalStates = q0 417 | 418 | val afa = new AFA (AFStateVar(0), 419 | Vector(postS, postQ0, postQ1, postQ2), 420 | finalStates) 421 | 422 | println(afa) 423 | 424 | print("Checking emptiness ... ") 425 | 426 | if (IsEmpty(afa)) 427 | println("empty") 428 | else 429 | println("not empty") 430 | } -------------------------------------------------------------------------------- /src/main/scala/AFormula.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sloth, an SMT solver for strings. 3 | * Copyright (C) 2017 Philipp Ruemmer, Petr Janku 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package strsolver 20 | 21 | import scala.collection.GenTraversableOnce 22 | 23 | // Symbolic representation of AFA symbol 24 | abstract class AFormula { 25 | import AFormula.widthOfChar 26 | 27 | override def toString: String = this match { 28 | case AFAnd(sub1: AFOr, sub2: AFOr) => "(" + sub1 + ") \u2227 (" + sub2 + ")" 29 | case AFAnd(sub1: AFOr, sub2) => "(" + sub1 + ") \u2227 (" + sub2 + ")" 30 | case AFAnd(sub1: AFAnd, sub2: AFOr) => sub1 + " \u2227 (" + sub2 + ")" 31 | case AFAnd(sub1, sub2: AFOr) => "(" + sub1 + ") \u2227 (" + sub2 + ")" 32 | case AFAnd(sub1, sub2) => sub1 + " \u2227 " + sub2 33 | case AFOr(sub1: AFOr, sub2: AFAnd) => sub1 + " \u2228 (" + sub2 + ")" 34 | case AFOr(sub1: AFAnd, sub2: AFOr) => "(" + sub1 + ") \u2228 " + sub2 35 | case AFOr(sub1: AFAnd, sub2: AFAnd) => "(" + sub1 + ") \u2228 (" + sub2 + ")" 36 | case AFOr(sub1, sub2) => sub1 + " \u2228 " + sub2 37 | case AFLet(sub1, sub2 : AFLet) => "[" + sub1 + "] " + sub2 38 | case AFLet(sub1, sub2) => "[" + sub1 + "] (" + sub2 + ")" 39 | case AFNot(sub: AFAnd) => "\u00AC(" + sub + ")" 40 | case AFNot(sub: AFOr) => "\u00AC(" + sub + ")" 41 | case AFNot(sub) => "\u00AC" + sub 42 | case AFStateVar(v) => "s_" + v 43 | case AFCharVar(v) => "a_" + v 44 | case AFSpecSymb(s) => "e_" + s 45 | case AFParam(p) => "p_" + p 46 | case AFDeBrujinVar(ind) => "_" + ind 47 | case AFFalse => "False" 48 | case AFTrue => "True" 49 | } 50 | 51 | def unary_~ : AFormula = this match { 52 | case AFTrue => AFFalse 53 | case AFFalse => AFTrue 54 | case AFNot(f) => f 55 | case _ => AFNot(this) 56 | } 57 | 58 | def &(sub: AFormula): AFormula = { 59 | if(this == AFFalse || sub == AFFalse || this == ~sub) 60 | return AFFalse 61 | 62 | if(this == AFTrue) 63 | return sub 64 | 65 | if(sub == AFTrue || this == sub) 66 | return this 67 | 68 | AFAnd(this, sub) 69 | } 70 | 71 | def |(sub: AFormula): AFormula = { 72 | if(this == AFTrue || sub == AFTrue || this == ~sub) 73 | return AFTrue 74 | 75 | if(this == AFFalse) 76 | return sub 77 | 78 | if(sub == AFFalse || this == sub) 79 | return this 80 | 81 | AFOr(this, sub) 82 | } 83 | 84 | def ==>(sub: AFormula): AFormula = 85 | ~this | sub 86 | 87 | def <=>(sub: AFormula): AFormula = 88 | (this & sub) | (~this & ~sub) 89 | 90 | def ^(sub: AFormula): AFormula = 91 | (this | sub) & (~this | ~sub) 92 | 93 | def letIn(that : AFormula) : AFormula = { 94 | if (that == AFTrue || that == AFFalse) 95 | that 96 | else 97 | AFLet(this, that) 98 | } 99 | 100 | def maxCharIndex : Int = this match { 101 | case AFAnd(s1, s2) => s1.maxCharIndex max s2.maxCharIndex 102 | case AFOr(s1, s2) => s1.maxCharIndex max s2.maxCharIndex 103 | case AFLet(s1, s2) => s1.maxCharIndex max s2.maxCharIndex 104 | case AFNot(s) => s.maxCharIndex 105 | case AFCharVar(v) => v 106 | case _ => 0 107 | } 108 | 109 | def charClasses : Set[Int] = this match { 110 | case AFAnd(s1, s2) => s1.charClasses ++ s2.charClasses 111 | case AFOr(s1, s2) => s1.charClasses ++ s2.charClasses 112 | case AFLet(s1, s2) => s1.charClasses ++ s2.charClasses 113 | case AFNot(s) => s.charClasses 114 | case AFCharVar(v) => Set(v / widthOfChar) 115 | case AFSpecSymb(v) => Set(v) 116 | case _ => Set() 117 | } 118 | 119 | def specSyms : Set[Int] = this match { 120 | case AFAnd(s1, s2) => s1.specSyms ++ s2.specSyms 121 | case AFOr(s1, s2) => s1.specSyms ++ s2.specSyms 122 | case AFLet(s1, s2) => s1.specSyms ++ s2.specSyms 123 | case AFNot(s) => s.specSyms 124 | case AFSpecSymb(ind) => Set(ind) 125 | case _ => Set() 126 | } 127 | 128 | def parameters : Set[Int] = this match { 129 | case AFAnd(s1, s2) => s1.parameters ++ s2.parameters 130 | case AFOr(s1, s2) => s1.parameters ++ s2.parameters 131 | case AFLet(s1, s2) => s1.parameters ++ s2.parameters 132 | case AFNot(s) => s.parameters 133 | case AFParam(p) => Set(p) 134 | case _ => Set() 135 | } 136 | 137 | private def getStates(f: AFormula): Set[Int] = f match { 138 | case AFAnd(sub1, sub2) => getStates(sub1) ++ getStates(sub2) 139 | case AFOr(sub1, sub2) => getStates(sub1) ++ getStates(sub2) 140 | case AFLet(sub1, sub2) => getStates(sub1) ++ getStates(sub2) 141 | case AFStateVar(v) => Set(v) 142 | case AFNot(s) => getStates(s) 143 | case _ => Set() 144 | } 145 | 146 | def getStates: Set[Int] = getStates(this) 147 | 148 | def shiftStateVariables(inc: Int): AFormula = this match { 149 | case AFAnd(sub1, sub2) => 150 | AFAnd(sub1 shiftStateVariables inc, sub2 shiftStateVariables inc) 151 | case AFOr(sub1, sub2) => 152 | AFOr(sub1 shiftStateVariables inc, sub2 shiftStateVariables inc) 153 | case AFLet(sub1, sub2) => 154 | AFLet(sub1 shiftStateVariables inc, sub2 shiftStateVariables inc) 155 | case AFNot(s) => 156 | AFNot(s shiftStateVariables inc) 157 | case AFStateVar(v) => 158 | AFStateVar(v + inc) 159 | case x => x 160 | } 161 | 162 | def permuteChars(mapping : Map[Int, Int]) : AFormula = this match { 163 | case AFAnd(sub1, sub2) => 164 | AFAnd(sub1 permuteChars mapping, sub2 permuteChars mapping) 165 | case AFOr(sub1, sub2) => 166 | AFOr(sub1 permuteChars mapping, sub2 permuteChars mapping) 167 | case AFLet(sub1, sub2) => 168 | AFLet(sub1 permuteChars mapping, sub2 permuteChars mapping) 169 | case AFNot(s) => 170 | AFNot(s permuteChars mapping) 171 | case AFCharVar(v) => { 172 | val varInd = v / widthOfChar 173 | val bit = v % widthOfChar 174 | AFCharVar(mapping(varInd) * widthOfChar + bit) 175 | } 176 | case AFSpecSymb(s) => 177 | AFSpecSymb(mapping(s)) 178 | case x => x 179 | } 180 | 181 | def renameVariables(s: Int, mapping : Map[Int, Int]): AFormula = this match { 182 | case AFAnd(sub1, sub2) => 183 | AFAnd(sub1.renameVariables(s, mapping), sub2.renameVariables(s, mapping)) 184 | case AFOr(sub1, sub2) => 185 | AFOr(sub1.renameVariables(s, mapping), sub2.renameVariables(s, mapping)) 186 | case AFLet(sub1, sub2) => 187 | AFLet(sub1.renameVariables(s, mapping), sub2.renameVariables(s, mapping)) 188 | case AFNot(sub) => 189 | AFNot(sub.renameVariables(s, mapping)) 190 | case AFStateVar(v) => 191 | AFStateVar(v + s) 192 | case AFCharVar(v) => { 193 | val varInd = v / widthOfChar 194 | val bit = v % widthOfChar 195 | AFCharVar(mapping(varInd) * widthOfChar + bit) 196 | } 197 | case AFSpecSymb(s) => 198 | AFSpecSymb(mapping(s)) 199 | case x => x 200 | } 201 | 202 | def paramToState(mapping : Map[Int, (Int, Int)], 203 | negated : Boolean) : AFormula = this match { 204 | case AFAnd(sub1, sub2) => 205 | AFAnd(sub1.paramToState(mapping, negated), 206 | sub2.paramToState(mapping, negated)) 207 | case AFOr(sub1, sub2) => 208 | AFOr(sub1.paramToState(mapping, negated), 209 | sub2.paramToState(mapping, negated)) 210 | case AFLet(sub1, sub2) => 211 | throw new IllegalArgumentException 212 | case AFNot(s) => 213 | AFNot(s.paramToState(mapping, !negated)) 214 | case AFParam(p) => 215 | if (negated) ~AFStateVar(mapping(p)._2) else AFStateVar(mapping(p)._1) 216 | case x => x 217 | } 218 | } 219 | 220 | object AFormula { 221 | val widthOfChar = 8 222 | 223 | def and(fors : Iterable[AFormula]) : AFormula = 224 | and(fors.iterator) 225 | 226 | def and(fors : Iterator[AFormula]) : AFormula = 227 | if (fors.hasNext) 228 | fors reduceLeft (_ & _) 229 | else 230 | AFTrue 231 | 232 | def or(fors : Iterable[AFormula]) : AFormula = 233 | or(fors.iterator) 234 | 235 | def or(fors : Iterator[AFormula]) : AFormula = 236 | if (fors.hasNext) 237 | fors reduceLeft (_ | _) 238 | else 239 | AFFalse 240 | 241 | /** 242 | * Bit-vector equality 243 | */ 244 | def bvEq(a : Seq[AFormula], b : Seq[AFormula]) : AFormula = 245 | and(for ((x, y) <- a.iterator zip b.iterator) yield (x <=> y)) 246 | 247 | // unsigned bit-vector comparison of two bit-strings, each 248 | // starting with the most-significant bit 249 | 250 | def bvLt(a : Seq[AFormula], b : Seq[AFormula]) = 251 | bvLess(a, b, false) 252 | 253 | def bvLeq(a : Seq[AFormula], b : Seq[AFormula]) = 254 | bvLess(a, b, true) 255 | 256 | def bvLess(ar : Seq[AFormula], br : Seq[AFormula], 257 | orEqual : Boolean) : AFormula = { 258 | assert(ar.size == br.size) 259 | 260 | val a = ar.reverse 261 | val b = br.reverse 262 | 263 | var curFor : AFormula = 264 | if (orEqual) 265 | AFDeBrujinVar(0) | AFDeBrujinVar(1) 266 | else 267 | AFDeBrujinVar(0) 268 | 269 | for (i <- a.indices) { 270 | curFor = 271 | (AFDeBrujinVar(1) | (~a(i) & b(i) & AFDeBrujinVar(2))) letIn curFor 272 | curFor = 273 | ((a(i) <=> b(i)) & AFDeBrujinVar(1)) letIn curFor 274 | } 275 | 276 | curFor = AFFalse letIn curFor 277 | curFor = AFTrue letIn curFor 278 | 279 | curFor 280 | } 281 | 282 | def nat2bv(value: Int): Seq[AFormula] = { 283 | var res: List[AFormula] = List.empty 284 | for (i <- 0 until AFormula.widthOfChar) { 285 | res = (if((value & (1 << i)) != 0) AFTrue else AFFalse) :: res 286 | } 287 | 288 | res 289 | } 290 | 291 | def bvSub(a: Seq[AFormula], b: Seq[AFormula]): Seq[AFormula] = { 292 | var tmp: AFormula = AFFalse 293 | var res: List[AFormula] = List.empty 294 | 295 | for(i <- a.indices.reverse) { 296 | res = (tmp ^ a(i) ^ b(i)) :: res 297 | 298 | tmp = (~a(i) & b(i)) | (tmp & ~a(i)) | (tmp & b(i)) 299 | } 300 | 301 | res 302 | } 303 | 304 | def bvAdd(a: Seq[AFormula], b: Seq[AFormula]): Seq[AFormula] = { 305 | var tmp: AFormula = AFFalse 306 | var res: List[AFormula] = List.empty 307 | 308 | for(i <- a.indices.reverse) { 309 | res = (tmp ^ a(i) ^ b(i)) :: res 310 | 311 | tmp = (tmp & a(i)) | (tmp & b(i)) | (a(i) & b(i)) 312 | } 313 | 314 | res 315 | } 316 | 317 | def bvShl(vec: Seq[AFormula], s: Int): Seq[AFormula] = { 318 | var res = vec 319 | 320 | for(i <- 0 until s) { 321 | res = res.tail :+ AFFalse 322 | } 323 | 324 | res 325 | } 326 | 327 | def createSymbol(char: Int, offset: Int): AFormula = { 328 | var index = 0x1 << widthOfChar - 1 329 | 330 | ((offset * widthOfChar) until (widthOfChar + widthOfChar * offset)).foldLeft[AFormula](AFTrue) { case (formula, bit) => 331 | val tmp = 332 | if((char & index) != 0) 333 | AFCharVar(bit) 334 | else 335 | AFNot(AFCharVar(bit)) 336 | 337 | index >>= 1 338 | formula & tmp 339 | } 340 | } 341 | 342 | def symbolInRange(valMin : Int, valMax : Int) : AFormula = { 343 | val vars = for (i <- 0 until widthOfChar) yield AFCharVar(i) 344 | val minBits = for (i <- 0 until widthOfChar) yield { 345 | if ((valMin & (1 << i)) != 0) AFTrue else AFFalse 346 | } 347 | val maxBits = for (i <- 0 until widthOfChar) yield { 348 | if ((valMax & (1 << i)) != 0) AFTrue else AFFalse 349 | } 350 | bvLeq(minBits.reverse, vars) & bvLeq(vars, maxBits.reverse) 351 | } 352 | 353 | /// 354 | /// With a specific track t 355 | /// 356 | def symbolInRange(valMin : Int, valMax : Int, t: Int = 0) : AFormula = { 357 | val vars = for (i <- 0 until widthOfChar) yield 358 | AFCharVar(i + t * widthOfChar) 359 | val minBits = for (i <- 0 until widthOfChar) yield { 360 | if ((valMin & (1 << i)) != 0) AFTrue else AFFalse 361 | } 362 | val maxBits = for (i <- 0 until widthOfChar) yield { 363 | if ((valMax & (1 << i)) != 0) AFTrue else AFFalse 364 | } 365 | bvLeq(minBits.reverse, vars) & bvLeq(vars, maxBits.reverse) 366 | } 367 | 368 | def createEpsilon(offset: Int): AFormula = 369 | AFSpecSymb(offset) & createSymbol(0, offset) 370 | def createSpecialSymbol(char: Int, offset: Int) = 371 | AFSpecSymb(offset) & createSymbol(char, offset) 372 | 373 | def apply(states: GenTraversableOnce[Int], neg: Boolean = false): AFormula = 374 | if(neg) 375 | states.foldLeft[AFormula](AFTrue) { case (formula, state) => formula & AFNot(AFStateVar(state)) } 376 | else 377 | states.foldLeft[AFormula](AFFalse) { case (formula, state) => formula | AFStateVar(state) } 378 | } 379 | 380 | case class AFAnd(sub1: AFormula, sub2: AFormula) extends AFormula 381 | case class AFOr(sub1: AFormula, sub2: AFormula) extends AFormula 382 | case class AFNot(sub: AFormula) extends AFormula 383 | case class AFLet(definition : AFormula, in : AFormula) extends AFormula 384 | case class AFStateVar(v: Int) extends AFormula 385 | case class AFCharVar(v: Int) extends AFormula 386 | case class AFSpecSymb(s: Int) extends AFormula 387 | case class AFParam(p: Int) extends AFormula 388 | case class AFDeBrujinVar(ind: Int) extends AFormula 389 | object AFFalse extends AFormula 390 | object AFTrue extends AFormula 391 | -------------------------------------------------------------------------------- /src/main/scala/AFA.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sloth, an SMT solver for strings. 3 | * Copyright (C) 2017 Philipp Ruemmer, Petr Janku 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package strsolver 20 | 21 | object AFA { 22 | import AFormula.widthOfChar 23 | 24 | /** 25 | * Construct the intersection of two AFA, with appropriate 26 | * mapping of their character variables. 27 | */ 28 | def synchronise(a : AFA, aCharMapping : Map[Int, Int], 29 | b : AFA, bCharMapping : Map[Int, Int], 30 | syncTrack : Int) : AFA = 31 | synchronise(a permuteChars aCharMapping, 32 | b permuteChars bCharMapping, 33 | syncTrack) 34 | 35 | /** 36 | * Construct the intersection of two AFA, with appropriate 37 | * mapping of their character variables. 38 | */ 39 | def synchronise(a : AFA, b : AFA, syncTrack : Int) : AFA = { 40 | def buildConstraint(c: Set[Int]) = 41 | c.foldLeft[AFormula](AFormula.createEpsilon(syncTrack)) { 42 | case (res, c) => res & AFormula.createEpsilon(c) 43 | } 44 | 45 | val aCharConstraint = 46 | if (b alwaysNonEps syncTrack) 47 | AFFalse 48 | else 49 | buildConstraint(a.charClasses - syncTrack) 50 | val bCharConstraint = 51 | if (a alwaysNonEps syncTrack) 52 | AFFalse 53 | else 54 | buildConstraint(b.charClasses - syncTrack) 55 | 56 | val newAStates = 57 | for ((s, num) <- a.states.zipWithIndex) 58 | yield (s | (aCharConstraint & AFStateVar(num))) 59 | val newBStates = 60 | for ((s, num) <- b.states.zipWithIndex) 61 | yield ((s | (bCharConstraint & AFStateVar(num))) 62 | .shiftStateVariables(a.states.size)) 63 | val newInitial = 64 | a.initialStates & 65 | b.initialStates.shiftStateVariables(a.states.size) 66 | val newFinal = 67 | a.finalStates & 68 | b.finalStates.shiftStateVariables(a.states.size) 69 | 70 | new AFA (newInitial, newAStates ++ newBStates, newFinal) 71 | } 72 | 73 | /** 74 | * Construct the union of two AFAs talking about distinct sets of 75 | * tracks. 76 | */ 77 | def conjoin(a : AFA, b : AFA) : AFA = { 78 | assert((a.charClasses & b.charClasses).isEmpty) 79 | 80 | def buildConstraint(c: Set[Int]) = 81 | AFormula.and(for (x <- c) yield AFormula.createEpsilon(x)) 82 | 83 | val aCharConstraint = buildConstraint(a.charClasses) 84 | val bCharConstraint = buildConstraint(b.charClasses) 85 | val newAStates = 86 | for ((s, num) <- a.states.zipWithIndex) 87 | yield (s | (aCharConstraint & AFStateVar(num))) 88 | val newBStates = 89 | for ((s, num) <- b.states.zipWithIndex) 90 | yield ((s | (bCharConstraint & AFStateVar(num))) 91 | .shiftStateVariables(a.states.size)) 92 | val newInitial = 93 | a.initialStates & 94 | b.initialStates.shiftStateVariables(a.states.size) 95 | val newFinal = 96 | a.finalStates & 97 | b.finalStates.shiftStateVariables(a.states.size) 98 | 99 | new AFA (newInitial, newAStates ++ newBStates, newFinal) 100 | } 101 | 102 | /** 103 | * Transducer expressing that two words are distinct. 104 | */ 105 | lazy val diffWordTransducer : AFA = { 106 | val vars0 = for (i <- 0 until widthOfChar) yield AFCharVar(i) 107 | val vars1 = for (i <- 0 until widthOfChar) yield AFCharVar(widthOfChar + i) 108 | val eqVars = AFormula.bvEq(vars0, vars1) 109 | 110 | val states = Vector( 111 | eqVars letIn ( 112 | (AFDeBrujinVar(0) & ~AFSpecSymb(0) & ~AFSpecSymb(1) & AFStateVar(0)) | 113 | (~AFDeBrujinVar(0) & ~AFSpecSymb(0) & ~AFSpecSymb(1) & AFStateVar(1)) | 114 | (AFormula.createEpsilon(0) & ~AFSpecSymb(1) & AFStateVar(2)) | 115 | (AFormula.createEpsilon(1) & ~AFSpecSymb(0) & AFStateVar(3)) 116 | ), 117 | (~AFSpecSymb(0) | ~AFSpecSymb(1)) & AFStateVar(1), 118 | ~AFSpecSymb(1) & AFStateVar(2), 119 | ~AFSpecSymb(0) & AFStateVar(3) 120 | ) 121 | 122 | val finalStates = 123 | AFormula.and(for (n <- 0 until states.size; if !(Set(1, 2, 3) contains n)) 124 | yield ~AFStateVar(n)) 125 | 126 | new AFA (AFStateVar(0), states, finalStates) 127 | } 128 | 129 | // I suppose that afa is transducer and therefore has only 2 tracks. 130 | def split(afa: AFA, vars: Seq[Map[Int, Int]]) = { 131 | def buildMemStates(num: Int, shift: Int): IndexedSeq[AFormula] = 132 | (shift until (num + shift)) map AFStateVar 133 | // def createMapping(track1: Int, track2: Int) = 134 | // Map((0 -> track1), (1 -> track2)) 135 | def buildEpsTrans(mapping: Map[Int, Int]) = 136 | mapping.foldLeft[AFormula](AFTrue) { case (res, (_, track)) => 137 | res & AFormula.createEpsilon(track); 138 | } 139 | //AFormula.createEpsilon(track1) & AFormula.createEpsilon(track2) 140 | def buildStates(states: Seq[AFormula], 141 | mapping : Map[Int, Int], 142 | shift: Int, 143 | epsTrans: AFormula) = 144 | states.zipWithIndex map { case (state, ind) => 145 | state.renameVariables(shift, mapping) | 146 | (AFStateVar(ind + shift) & epsTrans) 147 | } 148 | def buildPartOfInit(states: Range, memStates: Range) = { 149 | val part1 = states.foldLeft[AFormula](AFFalse) (_ | AFStateVar(_)) 150 | val part2 = states.view.zip(memStates).foldLeft[AFormula](AFTrue) { 151 | case (res, (s, m)) => 152 | res & (AFStateVar(s) ==> AFStateVar(m)) 153 | } 154 | 155 | part1 & part2 156 | } 157 | def buildPartOfFinal(states: Range, memStates: Range) = 158 | states.view.zip(memStates).foldLeft[AFormula](AFTrue) { 159 | case (res, (s, m)) => 160 | res & (AFStateVar(s) ==> AFStateVar(m)) 161 | } 162 | 163 | val oStates = afa.states // old states 164 | val size = oStates.size 165 | var initFormula = afa.initialStates 166 | var finalFormula: AFormula = AFTrue 167 | //var mapping = createMapping(vars.head._1, vars.head._2) 168 | var epsTrans = buildEpsTrans(vars.head) 169 | var states = oStates.zipWithIndex map { case (state, ind) => 170 | (state permuteChars vars.head) | (AFStateVar(ind) & epsTrans) } 171 | 172 | vars.tail foreach { case mapping => 173 | //mapping = createMapping(track1, track2) 174 | epsTrans = buildEpsTrans(mapping) 175 | finalFormula &= buildPartOfFinal((states.size - size) until states.size, 176 | states.size until (states.size + size)) 177 | states ++= buildMemStates(size, states.size) 178 | initFormula &= buildPartOfInit(states.size until (states.size + size), 179 | (states.size - size) until states.size) 180 | states ++= buildStates(oStates, mapping, states.size, epsTrans) 181 | } 182 | 183 | finalFormula &= afa.finalStates.shiftStateVariables(states.size - size) 184 | new AFA(initFormula, states, finalFormula) 185 | } 186 | 187 | /** 188 | * Automaton expressing the universal language. 189 | */ 190 | lazy val universalAutomaton : AFA = 191 | new AFA (AFStateVar(0), Vector(~AFSpecSymb(0) & AFStateVar(0)), AFTrue) 192 | 193 | } 194 | 195 | class AFA(val initialStates: AFormula, 196 | val states: Vector[AFormula], 197 | val finalStates: AFormula) { 198 | 199 | override def toString: String = { 200 | "numOfStates: " + states.size + "\n" + 201 | "initialStates: " + initialStates + "\n" + 202 | "finalStates: " + finalStates + "\n" + 203 | "States: \n" + 204 | states.view.zipWithIndex.foldLeft[String]("") { case (str, (transition, index)) => 205 | str + "state " + index + ":\n" + transition + "\n" 206 | } 207 | } 208 | 209 | lazy val maxCharIndex = 210 | (for (s <- states.iterator) yield s.maxCharIndex).max max 211 | finalStates.maxCharIndex 212 | 213 | lazy val charClasses = 214 | (for (s <- states.iterator ++ (Iterator single finalStates); 215 | c <- s.charClasses.iterator) yield c).toSet 216 | 217 | lazy val specSyms = 218 | (for (s <- states.iterator; ind <- s.specSyms.iterator) yield ind).toSet 219 | 220 | lazy val parameters : Set[Int] = 221 | initialStates.parameters ++ finalStates.parameters 222 | 223 | lazy val finalStateSet = finalStates.getStates 224 | 225 | private def alwaysNonEps(f : AFormula, neg : Boolean, 226 | offset : Int) : Boolean = f match { 227 | case AFAnd(f1, f2) if !neg => 228 | alwaysNonEps(f1, neg, offset) || alwaysNonEps(f2, neg, offset) 229 | case AFOr(f1, f2) if !neg => 230 | alwaysNonEps(f1, neg, offset) && alwaysNonEps(f2, neg, offset) 231 | case AFAnd(f1, f2) if neg => 232 | alwaysNonEps(f1, neg, offset) && alwaysNonEps(f2, neg, offset) 233 | case AFOr(f1, f2) if neg => 234 | alwaysNonEps(f1, neg, offset) || alwaysNonEps(f2, neg, offset) 235 | case AFSpecSymb(`offset`) if neg => 236 | true 237 | case AFFalse if !neg => 238 | true 239 | case AFTrue if neg => 240 | true 241 | case AFNot(f1) => 242 | alwaysNonEps(f1, !neg, offset) 243 | case AFLet(f1, f2) => 244 | alwaysNonEps(f2, neg, offset) 245 | case _ => 246 | false 247 | } 248 | 249 | def alwaysNonEps(offset : Int) : Boolean = 250 | states forall (alwaysNonEps(_, false, offset)) 251 | 252 | /** 253 | * Transform to an AFA with state 0 as the unique initial state. 254 | */ 255 | def normaliseInitial : AFA = initialStates match { 256 | case AFStateVar(0) => this 257 | case _ => { 258 | // add a new initial state, and turn previous initial state 259 | // formula into a transition 260 | val initStateTransition = 261 | (this.initialStates shiftStateVariables 1) & 262 | AFormula.and(for (c <- this.charClasses) 263 | yield AFormula.createEpsilon(c)) 264 | new AFA(AFStateVar(0), 265 | (List(initStateTransition) ++ 266 | (this.states map (_ shiftStateVariables 1))).toVector, 267 | ~AFStateVar(0) & (this.finalStates shiftStateVariables 1)) 268 | } 269 | } 270 | 271 | /** 272 | * Transform to an AFA without parameters, by adding additional states. 273 | * This corresponds to existential quantification of the parameters. 274 | */ 275 | def eliminateParameters : AFA = 276 | if (parameters.isEmpty) { 277 | this 278 | } else { 279 | assert(parameters forall (_ >= 0)) 280 | val paramSeq = parameters.toList.sorted 281 | val N = states.size 282 | 283 | // every parameter is mapped to one state for positive occurrences, 284 | // and a second state for negative occurrences 285 | val paramMapping = (for (p <- paramSeq.iterator) 286 | yield (p -> (N + 2*p, N + 2*p + 1))).toMap 287 | 288 | val newStates = 289 | states ++ 290 | (for (n <- 0 until 2*paramSeq.size) yield AFStateVar(N + n)).toVector 291 | val newInitial = 292 | initialStates.paramToState(paramMapping, false) & 293 | AFormula.and(for (n <- 0 until paramSeq.size) 294 | yield (AFStateVar(N + 2*n) | AFStateVar(N + 2*n + 1))) 295 | val newFinal = 296 | finalStates.paramToState(paramMapping, true) 297 | 298 | new AFA(newInitial, newStates, newFinal) 299 | } 300 | 301 | /** 302 | * Split the AFA into n components, using parameters with index 303 | * >= paramStartIndex to specify how initial and final states of the 304 | * components are connected. The result are n AFAs, and a new 305 | * parameter start index (index where unused parameters start). 306 | */ 307 | def parameterSplit(n : Int, 308 | paramStartIndex : Int) : (Seq[AFA], Int) = 309 | if (n == 1) { 310 | (List(this), paramStartIndex) 311 | } else { 312 | assert(n >= 2) 313 | val N = states.size 314 | 315 | val splitParams = 316 | for (i <- 1 until n) 317 | yield ((paramStartIndex + (i-1)*N) until (paramStartIndex + i*N)).toSeq 318 | 319 | val initialStateSeq = 320 | List(initialStates) ++ 321 | (for (params <- splitParams) 322 | yield AFormula.and(for (n <- 0 until N) 323 | yield (AFParam(params(n)) ==> AFStateVar(n)))) 324 | 325 | val finalStateSeq = 326 | (for (params <- splitParams) 327 | yield AFormula.and(for (n <- 0 until N) 328 | yield (AFStateVar(n) ==> AFParam(params(n))))) ++ 329 | List(finalStates) 330 | 331 | val afas = 332 | for ((init, fin) <- initialStateSeq zip finalStateSeq) 333 | yield new AFA(init, states, fin) 334 | (afas, paramStartIndex + (n-1)*N) 335 | } 336 | 337 | def shiftStateVariables(inc: Int): AFA = 338 | new AFA(initialStates shiftStateVariables inc, 339 | states.map(_.shiftStateVariables(inc)), 340 | finalStates shiftStateVariables inc) 341 | 342 | def And(afa: AFA): AFA = { 343 | def buildConstraint(c: Set[Int]) = 344 | c.foldLeft[AFormula](AFTrue) { 345 | case (res, c) => res & AFormula.createEpsilon(c) 346 | } 347 | 348 | val aCharConstraint = buildConstraint(this.charClasses) 349 | val bCharConstraint = buildConstraint(afa.charClasses) 350 | val newAStates = 351 | for ((s, num) <- this.states.zipWithIndex) 352 | yield ((s | (aCharConstraint & AFStateVar(num)))) 353 | val newBStates = 354 | for ((s, num) <- afa.states.zipWithIndex) 355 | yield ((s | (bCharConstraint & AFStateVar(num))) 356 | .shiftStateVariables(this.states.size)) 357 | val initFormula = this.initialStates & afa.initialStates.shiftStateVariables(this.states.size) 358 | val finalFormula = this.finalStates & afa.finalStates.shiftStateVariables(this.states.size) 359 | new AFA (initFormula, 360 | newAStates ++ newBStates, 361 | finalFormula) 362 | } 363 | 364 | def Or(afa: AFA): AFA = { 365 | val lAfa: AFA = shiftStateVariables(1) 366 | val rAfa: AFA = afa.shiftStateVariables(states.size + 1) 367 | val newFinalStates = 368 | if(lAfa.finalStateSet(0) && rAfa.finalStateSet(0)) 369 | rAfa.finalStates | lAfa.finalStates | AFStateVar(0) 370 | else 371 | rAfa.finalStates | lAfa.finalStates 372 | 373 | val nstates: Vector[AFormula] = 374 | (lAfa.states.head | rAfa.states.head) +: (lAfa.states ++ rAfa.states) 375 | 376 | new AFA(AFStateVar(0), nstates, newFinalStates) 377 | } 378 | 379 | def |(afa: AFA): AFA = { 380 | Or(afa) 381 | } 382 | 383 | def &(afa: AFA): AFA = { 384 | And(afa) 385 | } 386 | 387 | def permuteChars(mapping : Map[Int, Int]) : AFA = 388 | new AFA(initialStates, 389 | for (s <- states) yield (s permuteChars mapping), 390 | finalStates) 391 | } -------------------------------------------------------------------------------- /src/main/scala/StringTheoryTranslator.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sloth, an SMT solver for strings. 3 | * Copyright (C) 2017 Philipp Ruemmer, Petr Janku 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package strsolver 20 | 21 | import ap.parser._ 22 | import scala.collection.mutable.{HashSet => MHashSet} 23 | 24 | object StringTheoryTranslator { 25 | 26 | def apply(formula : IFormula, 27 | globalWordVariables : 28 | Iterable[IExpression.ConstantTerm]) : IFormula = { 29 | ~(new StringTheoryTranslator(~formula, List(), 30 | globalWordVariables)).newConstraint 31 | } 32 | 33 | } 34 | 35 | class StringTheoryTranslator private (constraint : IFormula, 36 | interestingAtoms : Seq[IAtom], 37 | globalWordVariables : 38 | Iterable[IExpression.ConstantTerm]) 39 | extends ContextAwareVisitor[Unit, IExpression] { 40 | 41 | import IExpression._ 42 | import SMTLIBStringTheory._ 43 | 44 | private val toPred = StringTheory.functionPredicateMapping.toMap 45 | 46 | private object SMTLIBPred { 47 | val reverseMapping = 48 | (for ((a, b) <- functionPredicateMapping.iterator) 49 | yield (b, a)).toMap 50 | def unapply(p : Predicate) : Option[IFunction] = 51 | reverseMapping get p 52 | } 53 | 54 | private object StringPred { 55 | val reverseMapping = 56 | (for ((a, b) <- toPred.iterator) 57 | yield (b, a)).toMap 58 | def unapply(p : Predicate) : Option[IFunction] = 59 | reverseMapping get p 60 | } 61 | 62 | ////////////////////////////////////////////////////////////////////////////// 63 | // Detect all character variables/constants in the constraint of the 64 | // formula 65 | 66 | private val charVariables = new MHashSet[IConstant] 67 | 68 | private object CharVariableDetector extends CollectingVisitor[Unit, Unit] { 69 | def postVisit(t : IExpression, arg : Unit, 70 | subres : Seq[Unit]) : Unit = t match { 71 | case IExpression.Eq(c : IConstant, d : IConstant) => 72 | if ((charVariables contains c) || 73 | (charVariables contains d)) { 74 | charVariables += c 75 | charVariables += d 76 | } 77 | case IAtom(SMTLIBPred(`seq_unit`), Seq(c : IConstant, _)) => 78 | charVariables += c 79 | case IAtom(SMTLIBPred(`seq_cons`), Seq(c : IConstant, _, _)) => 80 | charVariables += c 81 | case IAtom(SMTLIBPred(`seq_rev_cons`), Seq(_, c : IConstant, _)) => 82 | charVariables += c 83 | case IAtom(SMTLIBPred(`seq_head`), Seq(_, c : IConstant)) => 84 | charVariables += c 85 | case IAtom(SMTLIBPred(`seq_last`), Seq(_, c : IConstant)) => 86 | charVariables += c 87 | case IAtom(SMTLIBPred(`seq_nth`), Seq(_, _, c : IConstant)) => 88 | charVariables += c 89 | case IAtom(SMTLIBPred(`re_range`), Seq(c, d, _)) => { 90 | c match { 91 | case c : IConstant => charVariables += c 92 | case _ => // nothing 93 | } 94 | d match { 95 | case d : IConstant => charVariables += d 96 | case _ => // nothing 97 | } 98 | } 99 | case _ => // nothing 100 | } 101 | } 102 | 103 | { 104 | var oldSize = -1 105 | while (charVariables.size > oldSize) { 106 | oldSize = charVariables.size 107 | CharVariableDetector.visit(constraint, ()) 108 | } 109 | } 110 | 111 | ////////////////////////////////////////////////////////////////////////////// 112 | 113 | private def toTermSeq(s : Seq[IExpression]) = 114 | (for (e <- s.iterator) yield e.asInstanceOf[ITerm]).toIndexedSeq 115 | 116 | private var constCounter = 0 117 | private def newConstant = { 118 | val res = new ConstantTerm("c" + constCounter) 119 | constCounter = constCounter + 1 120 | res 121 | } 122 | 123 | private def polChoice(ctxt : Context[Unit]) 124 | (pos : => IFormula)(neg : => IFormula) : IFormula = 125 | if (ctxt.polarity > 0) { 126 | pos 127 | } else if (ctxt.polarity < 0) { 128 | neg 129 | } else { 130 | assert(false) 131 | null 132 | } 133 | 134 | private def guardedExpr(guard : IFormula, expr : IFormula, 135 | ctxt : Context[Unit]) : IFormula = 136 | polChoice(ctxt) { 137 | guard & expr 138 | } { 139 | guard ==> expr 140 | } 141 | 142 | ////////////////////////////////////////////////////////////////////////////// 143 | 144 | def postVisit(t : IExpression, ctxt : Context[Unit], 145 | subres : Seq[IExpression]) : IExpression = t match { 146 | // equations between characters have to be turned into 147 | // word equations 148 | case IExpression.Eq(c : IConstant, d) 149 | if (charVariables contains c) => { 150 | val a, b = newConstant 151 | guardedExpr(toPred(StringTheory.wordChar)(c, a) & 152 | toPred(StringTheory.wordChar)(d, b), 153 | a === b, 154 | ctxt) 155 | } 156 | case IExpression.Eq(d, c : IConstant) 157 | if (charVariables contains c) => { 158 | val a, b = newConstant 159 | guardedExpr(toPred(StringTheory.wordChar)(c, a) & 160 | toPred(StringTheory.wordChar)(d, b), 161 | a === b, 162 | ctxt) 163 | } 164 | 165 | case IAtom(SMTLIBPred(`seq_unit`), _) => 166 | IAtom(toPred(StringTheory.wordChar), toTermSeq(subres)) 167 | case IAtom(SMTLIBPred(`seq_empty`), _) => 168 | IAtom(toPred(StringTheory.wordEps), toTermSeq(subres)) 169 | case IAtom(SMTLIBPred(`seq_concat`), _) => 170 | IAtom(toPred(StringTheory.wordCat), toTermSeq(subres)) 171 | 172 | case IAtom(SMTLIBPred(`seq_cons`), _) => { 173 | val Seq(head, tail, res) = toTermSeq(subres) 174 | val c = newConstant 175 | guardedExpr(toPred(StringTheory.wordChar)(head, c), 176 | toPred(StringTheory.wordCat)(c, tail, res), 177 | ctxt) 178 | } 179 | case IAtom(SMTLIBPred(`seq_rev_cons`), _) => { 180 | val Seq(first, last, res) = toTermSeq(subres) 181 | val c = newConstant 182 | guardedExpr(toPred(StringTheory.wordChar)(last, c), 183 | toPred(StringTheory.wordCat)(first, c, res), 184 | ctxt) 185 | } 186 | case IAtom(SMTLIBPred(`seq_head`), _) => { 187 | val Seq(str, head) = toTermSeq(subres) 188 | val a, b, c = newConstant 189 | guardedExpr(toPred(StringTheory.wordChar)(c, a) & 190 | toPred(StringTheory.wordCat)(a, b, str), 191 | c === head, 192 | ctxt) 193 | } 194 | case IAtom(SMTLIBPred(`seq_tail`), _) => { 195 | val Seq(str, tail) = toTermSeq(subres) 196 | val a, b, sigma = newConstant 197 | guardedExpr(toPred(StringTheory.rexSigma)(sigma) & 198 | StringTheory.member(a, sigma) & 199 | toPred(StringTheory.wordCat)(a, b, str), 200 | b === tail, 201 | ctxt) 202 | } 203 | case IAtom(SMTLIBPred(`seq_last`), _) => { 204 | val Seq(str, last) = toTermSeq(subres) 205 | val a, b, c = newConstant 206 | guardedExpr(toPred(StringTheory.wordChar)(c, a) & 207 | toPred(StringTheory.wordCat)(b, a, str), 208 | c === last, 209 | ctxt) 210 | } 211 | case IAtom(SMTLIBPred(`seq_first`), _) => { 212 | val Seq(str, first) = toTermSeq(subres) 213 | val a, b, sigma = newConstant 214 | guardedExpr(toPred(StringTheory.rexSigma)(sigma) & 215 | StringTheory.member(a, sigma) & 216 | toPred(StringTheory.wordCat)(b, a, str), 217 | b === first, 218 | ctxt) 219 | } 220 | 221 | case IAtom(`seq_prefix_of`, _) => { 222 | val Seq(shorter, longer) = toTermSeq(subres) 223 | polChoice(ctxt) { 224 | toPred(StringTheory.wordCat)(shorter, newConstant, longer) 225 | } { 226 | val a, b, aLen, shorterLen, longerLen = newConstant 227 | (toPred(StringTheory.wordLen)(shorter, shorterLen) & 228 | toPred(StringTheory.wordLen)(longer, longerLen) & 229 | toPred(StringTheory.wordCat)(a, b, longer) & 230 | toPred(StringTheory.wordLen)(a, aLen) & 231 | (aLen <= shorterLen) & (aLen <= longerLen) & 232 | ((aLen === shorterLen) | (aLen === longerLen))) ==> 233 | (a === shorter) 234 | } 235 | } 236 | case IAtom(`seq_suffix_of`, _) => { 237 | val Seq(shorter, longer) = toTermSeq(subres) 238 | polChoice(ctxt) { 239 | toPred(StringTheory.wordCat)(newConstant, shorter, longer) 240 | } { 241 | val a, b, aLen, shorterLen, longerLen = newConstant 242 | (toPred(StringTheory.wordLen)(shorter, shorterLen) & 243 | toPred(StringTheory.wordLen)(longer, longerLen) & 244 | toPred(StringTheory.wordCat)(b, a, longer) & 245 | toPred(StringTheory.wordLen)(a, aLen) & 246 | (aLen <= shorterLen) & (aLen <= longerLen) & 247 | ((aLen === shorterLen) | (aLen === longerLen))) ==> 248 | (a === shorter) 249 | } 250 | } 251 | case IAtom(`seq_subseq_of`, _) => { 252 | val Seq(shorter, longer) = toTermSeq(subres) 253 | polChoice(ctxt) { 254 | val a, b, c = newConstant 255 | toPred(StringTheory.wordCat)(shorter, b, c) & 256 | toPred(StringTheory.wordCat)(a, c, longer) 257 | } { 258 | // this can be done if "shorter" is a concrete string; 259 | // for a word variable this looks difficult? 260 | assert(false) 261 | null 262 | } 263 | } 264 | 265 | case IAtom(SMTLIBPred(`seq_extract`), _) => { 266 | val Seq(full, lo, hi, res) = toTermSeq(subres) 267 | val pref, substr, b, c, prefLen, substrLen, fullLen = newConstant 268 | guardedExpr(toPred(StringTheory.wordCat)(substr, b, c) & 269 | toPred(StringTheory.wordCat)(pref, c, full) & 270 | toPred(StringTheory.wordLen)(pref, prefLen) & 271 | toPred(StringTheory.wordLen)(substr, substrLen) & 272 | toPred(StringTheory.wordLen)(full, fullLen) & 273 | ((lo >= 0) ==> 274 | ((prefLen <= lo) & (prefLen <= fullLen) & 275 | ((prefLen === lo) | (prefLen === fullLen)))) & 276 | ((lo < 0) ==> (prefLen === 0)) & 277 | (((hi >= lo) & (hi >= 0)) ==> 278 | ((prefLen + substrLen <= hi) & 279 | ((prefLen + substrLen === hi) | 280 | (prefLen + substrLen === fullLen)))) & 281 | (((hi < lo) | (hi < 0)) ==> (substrLen === 0)), 282 | substr === res, 283 | ctxt) 284 | } 285 | 286 | case IAtom(SMTLIBPred(`seq_length`), _) => 287 | IAtom(toPred(StringTheory.wordLen), toTermSeq(subres)) 288 | 289 | //////////////////////////////////////////////////////////////////////////// 290 | 291 | case IAtom(SMTLIBPred(`re_empty_set`), _) => 292 | IAtom(toPred(StringTheory.rexEmpty), toTermSeq(subres)) 293 | case IAtom(SMTLIBPred(`re_full_set`), _) => { 294 | val Seq(res) = toTermSeq(subres) 295 | val sigma = newConstant 296 | guardedExpr(toPred(StringTheory.rexSigma)(sigma), 297 | toPred(StringTheory.rexStar)(sigma, res), 298 | ctxt) 299 | } 300 | case IAtom(SMTLIBPred(`re_allchar`), _) => 301 | IAtom(toPred(StringTheory.rexSigma), toTermSeq(subres)) 302 | case IAtom(SMTLIBPred(`re_concat`), _) => 303 | IAtom(toPred(StringTheory.rexCat), toTermSeq(subres)) 304 | case IAtom(SMTLIBPred(`re_empty_seq`), _) => 305 | IAtom(toPred(StringTheory.rexEps), toTermSeq(subres)) 306 | 307 | case IAtom(SMTLIBPred(`re_star`), _) => 308 | IAtom(toPred(StringTheory.rexStar), toTermSeq(subres)) 309 | case IAtom(SMTLIBPred(`re_loop`), _) => 310 | assert(false); null 311 | case IAtom(SMTLIBPred(`re_plus`), _) => { 312 | val Seq(arg, res) = toTermSeq(subres) 313 | val a = newConstant 314 | guardedExpr(toPred(StringTheory.rexStar)(arg, a), 315 | toPred(StringTheory.rexCat)(arg, a, res), 316 | ctxt) 317 | } 318 | case IAtom(SMTLIBPred(`re_option`), _) => { 319 | val Seq(arg, res) = toTermSeq(subres) 320 | val a = newConstant 321 | guardedExpr(toPred(StringTheory.rexEps)(a), 322 | toPred(StringTheory.rexUnion)(arg, a, res), 323 | ctxt) 324 | } 325 | case IAtom(SMTLIBPred(`re_range`), _) => 326 | IAtom(toPred(StringTheory.rexRange), toTermSeq(subres)) 327 | 328 | case IAtom(SMTLIBPred(`re_union`), _) => 329 | IAtom(toPred(StringTheory.rexUnion), toTermSeq(subres)) 330 | case IAtom(SMTLIBPred(`re_difference`), _) => { 331 | val Seq(x, y, res) = toTermSeq(subres) 332 | val xNeg, xyNeg = newConstant 333 | guardedExpr(toPred(StringTheory.rexNeg)(x, xNeg) & 334 | toPred(StringTheory.rexUnion)(xNeg, y, xyNeg), 335 | toPred(StringTheory.rexNeg)(xyNeg, res), 336 | ctxt) 337 | } 338 | case IAtom(SMTLIBPred(`re_intersect`), _) => { 339 | val Seq(x, y, res) = toTermSeq(subres) 340 | val xNeg, yNeg, xyNeg = newConstant 341 | guardedExpr(toPred(StringTheory.rexNeg)(x, xNeg) & 342 | toPred(StringTheory.rexNeg)(y, yNeg) & 343 | toPred(StringTheory.rexUnion)(xNeg, yNeg, xyNeg), 344 | toPred(StringTheory.rexNeg)(xyNeg, res), 345 | ctxt) 346 | } 347 | case IAtom(SMTLIBPred(`re_complement`), _) => 348 | IAtom(toPred(StringTheory.rexNeg), toTermSeq(subres)) 349 | 350 | case IAtom(SMTLIBPred(`re_of_pred`), _) => 351 | assert(false); null 352 | 353 | case IAtom(`re_member`, _) => 354 | IAtom(StringTheory.member, toTermSeq(subres)) 355 | 356 | case IAtom(SMTLIBPred(`seq_replace`), _) => 357 | IAtom(toPred(StringTheory.replace), toTermSeq(subres)) 358 | 359 | case IAtom(SMTLIBPred(`seq_replace_all`), _) => 360 | IAtom(toPred(StringTheory.replaceall), toTermSeq(subres)) 361 | 362 | //////////////////////////////////////////////////////////////////////////// 363 | 364 | case t => 365 | t update subres 366 | } 367 | 368 | ////////////////////////////////////////////////////////////////////////////// 369 | 370 | private val preConstraint = 371 | visit(constraint, Context()).asInstanceOf[IFormula] 372 | 373 | ////////////////////////////////////////////////////////////////////////////// 374 | // Detect word variables that are used in word/regex context 375 | 376 | private val wordVariables = new MHashSet[IConstant] 377 | private val regexVariables = new MHashSet[IConstant] 378 | 379 | private object WordVariableDetector extends CollectingVisitor[Unit, Unit] { 380 | def postVisit(t : IExpression, arg : Unit, 381 | subres : Seq[Unit]) : Unit = t match { 382 | case IExpression.Eq(c : IConstant, d : IConstant) => { 383 | if ((wordVariables contains c) || (wordVariables contains d)) { 384 | wordVariables += c 385 | wordVariables += d 386 | } 387 | if ((regexVariables contains c) || (regexVariables contains d)) { 388 | regexVariables += c 389 | regexVariables += d 390 | } 391 | } 392 | 393 | case IAtom(SMTLIBPred(`re_of_seq`), 394 | Seq(c : IConstant, _)) => 395 | regexVariables += c 396 | case IAtom(StringPred(StringTheory.replace | StringTheory.replaceall), 397 | args) => 398 | for (c <- args) c match { 399 | case c : IConstant => wordVariables += c 400 | case _ => // nothing 401 | } 402 | case IAtom(StringPred(StringTheory.wordLen), 403 | Seq(c : IConstant, _)) => 404 | wordVariables += c 405 | case IAtom(StringTheory.member, 406 | Seq(c : IConstant, _)) => 407 | wordVariables += c 408 | 409 | case IAtom(StringPred(StringTheory.wordCat), 410 | Seq(c : IConstant, d : IConstant, e : IConstant)) => { 411 | if ((wordVariables contains c) || 412 | (wordVariables contains d) || 413 | (wordVariables contains e)) { 414 | wordVariables += c 415 | wordVariables += d 416 | wordVariables += e 417 | } 418 | if (regexVariables contains e) { 419 | regexVariables += c 420 | regexVariables += d 421 | } 422 | } 423 | 424 | case _ => // nothing 425 | } 426 | } 427 | 428 | for (c <- globalWordVariables) 429 | wordVariables += IConstant(c) 430 | 431 | for (a <- interestingAtoms.iterator; 432 | c@IConstant(_) <- a.args.iterator) 433 | wordVariables += c 434 | 435 | { 436 | var oldWordSize = -1 437 | var oldRegexSize = -1 438 | while (wordVariables.size > oldWordSize || 439 | regexVariables.size > oldRegexSize) { 440 | oldWordSize = wordVariables.size 441 | oldRegexSize = regexVariables.size 442 | WordVariableDetector.visit(preConstraint, ()) 443 | } 444 | } 445 | 446 | ////////////////////////////////////////////////////////////////////////////// 447 | // Duplicate/replace word constraints that are used in the context of 448 | // regular expressions 449 | 450 | private val wordVariableDupl = 451 | (for (d@IConstant(c) <- wordVariables.iterator; 452 | if (regexVariables contains d)) 453 | yield (c -> IExpression.i(newConstant))).toMap 454 | 455 | private object WordVariableDuplicator extends CollectingVisitor[Unit, IExpression] { 456 | def postVisit(t : IExpression, arg : Unit, 457 | subres : Seq[IExpression]) : IExpression = t match { 458 | case IExpression.EqZ(_) => { 459 | val f = (t update subres).asInstanceOf[IFormula] 460 | val mapped = ConstantSubstVisitor(f, wordVariableDupl) 461 | if (mapped == f) f else f & mapped 462 | } 463 | case f@IAtom(StringPred(StringTheory.wordEps), Seq(c : IConstant)) => 464 | and((if ((wordVariables contains c) || 465 | !(regexVariables contains c)) List(f update subres) else List()) ++ 466 | (if (regexVariables contains c) 467 | List(ConstantSubstVisitor(IAtom(toPred(StringTheory.rexEps), 468 | toTermSeq(subres)), 469 | wordVariableDupl)) 470 | else List())) 471 | case f@IAtom(StringPred(StringTheory.wordChar), Seq(_, c : IConstant)) => 472 | and((if ((wordVariables contains c) || 473 | !(regexVariables contains c)) List(f update subres) else List()) ++ 474 | (if (regexVariables contains c) 475 | List(ConstantSubstVisitor(IAtom(toPred(StringTheory.rexChar), 476 | toTermSeq(subres)), 477 | wordVariableDupl)) 478 | else List())) 479 | case f@IAtom(StringPred(StringTheory.wordCat), Seq(_, _, c : IConstant)) => 480 | and((if ((wordVariables contains c) || 481 | !(regexVariables contains c)) List(f update subres) else List()) ++ 482 | (if (regexVariables contains c) 483 | List(ConstantSubstVisitor(IAtom(toPred(StringTheory.rexCat), 484 | toTermSeq(subres)), 485 | wordVariableDupl)) 486 | else List())) 487 | case IAtom(SMTLIBPred(`re_of_seq`), 488 | Seq(IConstant(c), d)) => 489 | (wordVariableDupl get c) match { 490 | case Some(e) => e === d 491 | case None => c === d 492 | } 493 | case t => 494 | t update subres 495 | } 496 | } 497 | 498 | val newConstraint = 499 | WordVariableDuplicator.visit(preConstraint, ()).asInstanceOf[IFormula] 500 | 501 | } 502 | -------------------------------------------------------------------------------- /tests/Answers: -------------------------------------------------------------------------------- 1 | 2 | simple-cvc-smtlib.smt2 3 | Reading file simple-cvc-smtlib.smt2 ... 4 | Assuming bit-vectors of width 8 5 | 6 | Running AFA-based consistency check 7 | Using straight-line solver 8 | After splitting and topological sorting: 3 AFAs 9 | After treeification: 2 trees 10 | Final AFA has 10 states ... is non-empty! 11 | sat 12 | 13 | (error "Internal exception: java.lang.AssertionError: assertion failed") 14 | 15 | simple-cycle.smt2 16 | Reading file simple-cycle.smt2 ... 17 | Assuming bit-vectors of width 8 18 | 19 | unsat 20 | 21 | simple-cycle2.smt2 22 | Reading file simple-cycle2.smt2 ... 23 | Assuming bit-vectors of width 8 24 | 25 | unsat 26 | 27 | nonlinear.smt2 28 | Reading file nonlinear.smt2 ... 29 | Assuming bit-vectors of width 8 30 | 31 | Running AFA-based consistency check 32 | Using straight-line solver 33 | After splitting and topological sorting: 2 AFAs 34 | After treeification: 1 trees 35 | Final AFA has 6 states ... is empty! 36 | unsat 37 | 38 | nonlinear-2.smt2 39 | Reading file nonlinear-2.smt2 ... 40 | Assuming bit-vectors of width 8 41 | 42 | Running AFA-based consistency check 43 | Using straight-line solver 44 | After splitting and topological sorting: 2 AFAs 45 | After treeification: 1 trees 46 | Final AFA has 4 states ... is non-empty! 47 | sat 48 | 49 | (define-fun a () String "))") 50 | (define-fun b () String ")") 51 | 52 | norn-benchmark-9.smt2 53 | Reading file norn-benchmark-9.smt2 ... 54 | Assuming bit-vectors of width 8 55 | 56 | Running AFA-based consistency check 57 | Using tree-based solver 58 | Solving constraints: 59 | Left(P0) 60 | Right(member(P0, P2)) 61 | Right(!member(P0, P5)) 62 | 63 | Resulting AFA over variables P0 has 4 states ... is empty! 64 | unsat 65 | 66 | norn-benchmark-9b.smt2 67 | Reading file norn-benchmark-9b.smt2 ... 68 | Assuming bit-vectors of width 8 69 | 70 | Running AFA-based consistency check 71 | Using tree-based solver 72 | Solving constraints: 73 | Left(var_0) 74 | Right(member(var_0, P1)) 75 | Right(!member(var_0, P4)) 76 | 77 | Resulting AFA over variables var_0 has 3 states ... is non-empty! 78 | sat 79 | 80 | (define-fun var_0 () String "a") 81 | 82 | norn-benchmark-9c.smt2 83 | Reading file norn-benchmark-9c.smt2 ... 84 | Assuming bit-vectors of width 8 85 | 86 | Running AFA-based consistency check 87 | Using tree-based solver 88 | Solving constraints: 89 | Left(var_0) 90 | Right(member(var_0, P4)) 91 | Right(member(var_0, P1)) 92 | 93 | Resulting AFA over variables var_0 has 2 states ... is non-empty! 94 | sat 95 | 96 | (define-fun var_0 () String "") 97 | 98 | norn-benchmark-9d.smt2 99 | Reading file norn-benchmark-9d.smt2 ... 100 | Assuming bit-vectors of width 8 101 | 102 | Running AFA-based consistency check 103 | Using tree-based solver 104 | Solving constraints: 105 | Left(var_0) 106 | Right(member(var_0, P5)) 107 | Right(member(var_0, P2)) 108 | 109 | Resulting AFA over variables var_0 has 3 states ... is empty! 110 | unsat 111 | 112 | norn-benchmark-9e.smt2 113 | Reading file norn-benchmark-9e.smt2 ... 114 | Assuming bit-vectors of width 8 115 | 116 | Running AFA-based consistency check 117 | Using tree-based solver 118 | Solving constraints: 119 | Left(var_0) 120 | Right(member(var_0, P4)) 121 | Right(member(var_0, P1)) 122 | 123 | Resulting AFA over variables var_0 has 3 states ... is empty! 124 | unsat 125 | 126 | norn-benchmark-9f.smt2 127 | Reading file norn-benchmark-9f.smt2 ... 128 | Assuming bit-vectors of width 8 129 | 130 | Running AFA-based consistency check 131 | Using tree-based solver 132 | Solving constraints: 133 | Left(var_0) 134 | Right(wordDiff(var_0, P5)) 135 | Left(P5) 136 | Right(member(var_0, P4)) 137 | Right(member(var_0, P1)) 138 | 139 | Resulting AFA over variables var_0, P5 has 8 states ... is empty! 140 | unsat 141 | 142 | norn-benchmark-9g.smt2 143 | Reading file norn-benchmark-9g.smt2 ... 144 | Assuming bit-vectors of width 8 145 | 146 | Running AFA-based consistency check 147 | Using tree-based solver 148 | sat 149 | 150 | 151 | norn-benchmark-9h.smt2 152 | Reading file norn-benchmark-9h.smt2 ... 153 | Assuming bit-vectors of width 8 154 | 155 | Running AFA-based consistency check 156 | Using tree-based solver 157 | Solving constraints: 158 | Left(var_1) 159 | Right(wordDiff(var_1, var_0)) 160 | Left(var_0) 161 | Right(member(var_0, P2)) 162 | Right(member(var_1, P2)) 163 | 164 | Resulting AFA over variables var_1, var_0 has 12 states ... is empty! 165 | unsat 166 | 167 | norn-benchmark-9i.smt2 168 | Reading file norn-benchmark-9i.smt2 ... 169 | Assuming bit-vectors of width 8 170 | 171 | Running AFA-based consistency check 172 | Using tree-based solver 173 | Solving constraints: 174 | Left(var_1) 175 | Right(wordDiff(var_1, var_0)) 176 | Left(var_0) 177 | Right(member(var_0, P2)) 178 | Right(member(var_1, P13)) 179 | 180 | Resulting AFA over variables var_1, var_0 has 13 states ... is non-empty! 181 | sat 182 | 183 | (define-fun var_0 () String "abc") 184 | (define-fun var_1 () String "abcd") 185 | 186 | norn-benchmark-9j.smt2 187 | Reading file norn-benchmark-9j.smt2 ... 188 | Assuming bit-vectors of width 8 189 | 190 | Running AFA-based consistency check 191 | Using tree-based solver 192 | Solving constraints: 193 | Left(var_0) 194 | Right(member(var_0, P13)) 195 | Right(member(var_0, P2)) 196 | 197 | Resulting AFA over variables var_0 has 9 states ... is empty! 198 | Running AFA-based consistency check 199 | Using tree-based solver 200 | Solving constraints: 201 | Left(var_0) 202 | Right(member(var_0, P6)) 203 | Right(member(var_0, P2)) 204 | 205 | Resulting AFA over variables var_0 has 8 states ... is empty! 206 | Running AFA-based consistency check 207 | Using tree-based solver 208 | Solving constraints: 209 | Left(var_0) 210 | Right(member(var_0, P9)) 211 | Right(member(var_0, P2)) 212 | 213 | Resulting AFA over variables var_0 has 7 states ... is empty! 214 | unsat 215 | 216 | norn-benchmark-9k.smt2 217 | Reading file norn-benchmark-9k.smt2 ... 218 | Assuming bit-vectors of width 8 219 | 220 | unsat 221 | 222 | simple-concat.smt2 223 | Reading file simple-concat.smt2 ... 224 | Parsing transducer (2 tracks): 225 | 226 | State toUpper: 227 | (accepting) 228 | -> toUpper: seq_head(_0) = \if ((bv_ule(8, 97.\as[bv[8]], seq_head(_1)) & bv_ule(8, seq_head(_1), 122.\as[bv[8]]))) \ then (bv_sub(8, seq_head(_1), 32.\as[bv[8]])) \ else (seq_head(_1)) 229 | 230 | Assuming bit-vectors of width 8 231 | 232 | Running AFA-based consistency check 233 | Using straight-line solver 234 | After splitting and topological sorting: 4 AFAs 235 | After treeification: 2 trees 236 | Final AFA has 8 states ... is non-empty! 237 | sat 238 | 239 | (define-fun x0 () String "h") 240 | (define-fun x1 () String "") 241 | (define-fun x2 () String "h") 242 | (define-fun x3 () String "H") 243 | 244 | simple-concat-2.smt2 245 | Reading file simple-concat-2.smt2 ... 246 | Parsing transducer (2 tracks): 247 | 248 | State toUpper: 249 | (accepting) 250 | -> toUpper: seq_head(_0) = \if ((bv_ule(8, 97.\as[bv[8]], seq_head(_1)) & bv_ule(8, seq_head(_1), 122.\as[bv[8]]))) \ then (bv_sub(8, seq_head(_1), 32.\as[bv[8]])) \ else (seq_head(_1)) 251 | 252 | Assuming bit-vectors of width 8 253 | 254 | Running AFA-based consistency check 255 | Using straight-line solver 256 | After splitting and topological sorting: 4 AFAs 257 | After treeification: 2 trees 258 | Final AFA has 8 states ... is empty! 259 | unsat 260 | 261 | simple-concat-3.smt2 262 | Reading file simple-concat-3.smt2 ... 263 | Parsing transducer (2 tracks): 264 | 265 | State toUpper: 266 | (accepting) 267 | -> toUpper: seq_head(_0) = \if ((bv_ule(8, 97.\as[bv[8]], seq_head(_1)) & bv_ule(8, seq_head(_1), 122.\as[bv[8]]))) \ then (bv_sub(8, seq_head(_1), 32.\as[bv[8]])) \ else (seq_head(_1)) 268 | 269 | Assuming bit-vectors of width 8 270 | 271 | Running AFA-based consistency check 272 | Using straight-line solver 273 | After splitting and topological sorting: 4 AFAs 274 | After treeification: 2 trees 275 | Final AFA has 7 states ... is empty! 276 | unsat 277 | 278 | simple-concat-4.smt2 279 | Reading file simple-concat-4.smt2 ... 280 | Parsing transducer (2 tracks): 281 | 282 | State toUpper: 283 | (accepting) 284 | -> toUpper: seq_head(_0) = \if ((bv_ule(8, 97.\as[bv[8]], seq_head(_1)) & bv_ule(8, seq_head(_1), 122.\as[bv[8]]))) \ then (bv_sub(8, seq_head(_1), 32.\as[bv[8]])) \ else (seq_head(_1)) 285 | 286 | Assuming bit-vectors of width 8 287 | 288 | Running AFA-based consistency check 289 | Using straight-line solver 290 | After splitting and topological sorting: 3 AFAs 291 | After treeification: 2 trees 292 | Final AFA has 6 states ... is non-empty! 293 | sat 294 | 295 | (define-fun x1 () String "b") 296 | (define-fun x2 () String "") 297 | (define-fun x3 () String "b") 298 | 299 | simple-concat-4b.smt2 300 | Reading file simple-concat-4b.smt2 ... 301 | Parsing transducer (2 tracks): 302 | 303 | State toUpper: 304 | (accepting) 305 | -> toUpper: seq_head(_0) = \if ((bv_ule(8, 97.\as[bv[8]], seq_head(_1)) & bv_ule(8, seq_head(_1), 122.\as[bv[8]]))) \ then (bv_sub(8, seq_head(_1), 32.\as[bv[8]])) \ else (seq_head(_1)) 306 | 307 | Assuming bit-vectors of width 8 308 | 309 | Running AFA-based consistency check 310 | Using straight-line solver 311 | After splitting and topological sorting: 3 AFAs 312 | After treeification: 2 trees 313 | Final AFA has 6 states ... is empty! 314 | unsat 315 | 316 | simple-concat-5.smt2 317 | Reading file simple-concat-5.smt2 ... 318 | Parsing transducer (2 tracks): 319 | 320 | State toUpper: 321 | (accepting) 322 | -> toUpper: seq_head(_0) = \if ((bv_ule(8, 97.\as[bv[8]], seq_head(_1)) & bv_ule(8, seq_head(_1), 122.\as[bv[8]]))) \ then (bv_sub(8, seq_head(_1), 32.\as[bv[8]])) \ else (seq_head(_1)) 323 | 324 | Assuming bit-vectors of width 8 325 | 326 | Running AFA-based consistency check 327 | Using straight-line solver 328 | After splitting and topological sorting: 4 AFAs 329 | After treeification: 2 trees 330 | Final AFA has 19 states ... is empty! 331 | unsat 332 | 333 | simple-concat-5b.smt2 334 | Reading file simple-concat-5b.smt2 ... 335 | Parsing transducer (2 tracks): 336 | 337 | State toUpper: 338 | (accepting) 339 | -> toUpper: seq_head(_0) = \if ((bv_ule(8, 97.\as[bv[8]], seq_head(_1)) & bv_ule(8, seq_head(_1), 122.\as[bv[8]]))) \ then (bv_sub(8, seq_head(_1), 32.\as[bv[8]])) \ else (seq_head(_1)) 340 | 341 | Assuming bit-vectors of width 8 342 | 343 | Running AFA-based consistency check 344 | Using straight-line solver 345 | After splitting and topological sorting: 4 AFAs 346 | After treeification: 2 trees 347 | Final AFA has 18 states ... is non-empty! 348 | sat 349 | 350 | (define-fun x0 () String "p") 351 | (define-fun x2 () String "pHelloWorld") 352 | (define-fun x3 () String "PHELLOWORLD") 353 | 354 | concat.smt2 355 | Reading file concat.smt2 ... 356 | Parsing transducer (2 tracks): 357 | 358 | State toUpper: 359 | (accepting) 360 | -> toUpper: seq_head(_0) = \if ((bv_ule(8, 97.\as[bv[8]], seq_head(_1)) & bv_ule(8, seq_head(_1), 122.\as[bv[8]]))) \ then (bv_sub(8, seq_head(_1), 32.\as[bv[8]])) \ else (seq_head(_1)) 361 | 362 | Parsing transducer (2 tracks): 363 | 364 | State simple: 365 | -> state1: seq_head(_1) = 104.\as[bv[8]] & seq_head(_0) = 119.\as[bv[8]] 366 | 367 | State state1: 368 | -> state2: seq_head(_1) = 101.\as[bv[8]] & seq_head(_0) = 111.\as[bv[8]] 369 | 370 | State state2: 371 | -> state3: seq_head(_1) = 108.\as[bv[8]] & seq_head(_0) = 114.\as[bv[8]] 372 | 373 | State state3: 374 | -> state4: seq_head(_1) = 108.\as[bv[8]] & seq_head(_0) = 108.\as[bv[8]] 375 | 376 | State state4: 377 | -> state5: seq_head(_1) = 111.\as[bv[8]] & seq_head(_0) = 100.\as[bv[8]] 378 | 379 | State state5: 380 | (accepting) 381 | 382 | Assuming bit-vectors of width 8 383 | 384 | Running AFA-based consistency check 385 | Using straight-line solver 386 | After splitting and topological sorting: 22 AFAs 387 | After treeification: 8 trees 388 | Final AFA has 106 states ... is empty! 389 | unsat 390 | 391 | simple-replace.smt2 392 | Reading file simple-replace.smt2 ... 393 | Warning: get-model detected, producing model 394 | Assuming bit-vectors of width 8 395 | 396 | Running AFA-based consistency check 397 | Using tree-based solver 398 | Solving constraints: 399 | Left(P9) 400 | Right(replaceall(P9, c7, P11, P12)) 401 | Left(P12) 402 | 403 | Resulting AFA over variables P9, P9P12_0, P12 has 21 states ... is non-empty! 404 | sat 405 | 406 | (define-fun x_7 () String "HalloWorld") 407 | 408 | simple-replace-1b.smt2 409 | Reading file simple-replace-1b.smt2 ... 410 | Warning: get-model detected, producing model 411 | Assuming bit-vectors of width 8 412 | 413 | Running AFA-based consistency check 414 | Using tree-based solver 415 | Solving constraints: 416 | Left(P9) 417 | Right(replaceall(P9, c7, P11, P12)) 418 | Left(P12) 419 | 420 | Resulting AFA over variables P9, P9P12_0, P12 has 32 states ... is empty! 421 | unsat 422 | 423 | simple-replace-1c.smt2 424 | Reading file simple-replace-1c.smt2 ... 425 | Warning: get-model detected, producing model 426 | Assuming bit-vectors of width 8 427 | 428 | Running AFA-based consistency check 429 | Using tree-based solver 430 | Solving constraints: 431 | Left(P9) 432 | Right(replaceall(P9, c7, P11, P12)) 433 | Left(P12) 434 | 435 | Resulting AFA over variables P9, P9P12_0, P12 has 32 states ... is non-empty! 436 | sat 437 | 438 | (define-fun x_7 () String "HalloWorld") 439 | 440 | simple-replace-1d.smt2 441 | Reading file simple-replace-1d.smt2 ... 442 | Warning: get-model detected, producing model 443 | Assuming bit-vectors of width 8 444 | 445 | Running AFA-based consistency check 446 | Using tree-based solver 447 | Solving constraints: 448 | Left(P9) 449 | Right(replace(P9, c7, P11, P12)) 450 | Left(P12) 451 | 452 | Resulting AFA over variables P9, P12 has 27 states ... is empty! 453 | unsat 454 | 455 | simple-replace-1e.smt2 456 | Reading file simple-replace-1e.smt2 ... 457 | Warning: get-model detected, producing model 458 | Assuming bit-vectors of width 8 459 | 460 | Running AFA-based consistency check 461 | Using tree-based solver 462 | Solving constraints: 463 | Left(P9) 464 | Right(replace(P9, c7, P11, P12)) 465 | Left(P12) 466 | 467 | Resulting AFA over variables P9, P12 has 18 states ... is non-empty! 468 | sat 469 | 470 | (define-fun x_7 () String "HalloWorld") 471 | 472 | simple-replace-1f.smt2 473 | Reading file simple-replace-1f.smt2 ... 474 | Warning: get-model detected, producing model 475 | Assuming bit-vectors of width 8 476 | 477 | Running AFA-based consistency check 478 | Using tree-based solver 479 | Solving constraints: 480 | Left(P9) 481 | Right(replace(P9, c7, P11, P12)) 482 | Left(P12) 483 | 484 | Resulting AFA over variables P9, P12 has 29 states ... is empty! 485 | unsat 486 | 487 | simple-replace-1g.smt2 488 | Reading file simple-replace-1g.smt2 ... 489 | Warning: get-model detected, producing model 490 | Assuming bit-vectors of width 8 491 | 492 | Running AFA-based consistency check 493 | Using tree-based solver 494 | Solving constraints: 495 | Left(P9) 496 | Right(replace(P9, c7, P11, P12)) 497 | Left(P12) 498 | 499 | Resulting AFA over variables P9, P12 has 29 states ... is non-empty! 500 | sat 501 | 502 | (define-fun x_7 () String "HalloWorld") 503 | 504 | simple-replace-2.smt2 505 | Reading file simple-replace-2.smt2 ... 506 | Warning: get-model detected, producing model 507 | Assuming bit-vectors of width 8 508 | 509 | Running AFA-based consistency check 510 | Using straight-line solver 511 | After splitting and topological sorting: 4 AFAs 512 | After treeification: 2 trees 513 | Final AFA has 22 states ... is non-empty! 514 | sat 515 | 516 | (define-fun a () String "") 517 | (define-fun b () String "") 518 | (define-fun x_10 () String "") 519 | (define-fun x_7 () String "") 520 | 521 | simple-replace-2b.smt2 522 | Reading file simple-replace-2b.smt2 ... 523 | Warning: get-model detected, producing model 524 | Assuming bit-vectors of width 8 525 | 526 | Running AFA-based consistency check 527 | Using straight-line solver 528 | After splitting and topological sorting: 8 AFAs 529 | After treeification: 2 trees 530 | Final AFA has 42 states ... is empty! 531 | Running AFA-based consistency check 532 | Using straight-line solver 533 | After splitting and topological sorting: 6 AFAs 534 | After treeification: 2 trees 535 | Final AFA has 36 states ... is empty! 536 | unsat 537 | 538 | simple-replace-2c.smt2 539 | Reading file simple-replace-2c.smt2 ... 540 | Warning: get-model detected, producing model 541 | Assuming bit-vectors of width 8 542 | 543 | Running AFA-based consistency check 544 | Using straight-line solver 545 | After splitting and topological sorting: 8 AFAs 546 | After treeification: 2 trees 547 | Final AFA has 42 states ... is non-empty! 548 | sat 549 | 550 | (define-fun a () String "He") 551 | (define-fun b () String "llo") 552 | (define-fun x_10 () String "Hello") 553 | (define-fun x_7 () String "Hallo") 554 | 555 | simple-replace-2d.smt2 556 | Reading file simple-replace-2d.smt2 ... 557 | Warning: get-model detected, producing model 558 | Assuming bit-vectors of width 8 559 | 560 | Running AFA-based consistency check 561 | Using straight-line solver 562 | After splitting and topological sorting: 2 AFAs 563 | After treeification: 2 trees 564 | Final AFA has 14 states ... is non-empty! 565 | sat 566 | 567 | (define-fun a () String "") 568 | (define-fun b () String "") 569 | (define-fun x_10 () String "") 570 | (define-fun x_7 () String "") 571 | 572 | simple-replace-3.smt2 573 | Reading file simple-replace-3.smt2 ... 574 | Warning: get-model detected, producing model 575 | Assuming bit-vectors of width 8 576 | 577 | Running AFA-based consistency check 578 | Using tree-based solver 579 | Solving constraints: 580 | Left(x_10) 581 | Right(replaceall(x_10, P0, P1, P2)) 582 | Left(P2) 583 | Right(member(P2, P6)) 584 | 585 | Resulting AFA over variables x_10, x_10P2_0, P2 has 11 states ... is empty! 586 | unsat 587 | 588 | simple-replace-3b.smt2 589 | Reading file simple-replace-3b.smt2 ... 590 | Warning: get-model detected, producing model 591 | Assuming bit-vectors of width 8 592 | 593 | Running AFA-based consistency check 594 | Using tree-based solver 595 | Solving constraints: 596 | Left(x_10) 597 | Right(replace(x_10, P0, P1, P2)) 598 | Left(P2) 599 | Right(member(P2, P6)) 600 | 601 | Resulting AFA over variables x_10, P2 has 8 states ... is non-empty! 602 | sat 603 | 604 | (define-fun x_10 () String "ee") 605 | (define-fun x_7 () String "Xe") 606 | 607 | simple-replace-4.smt2 608 | Reading file simple-replace-4.smt2 ... 609 | Warning: ignoring option :strings-exp 610 | Warning: get-model detected, producing model 611 | Assuming bit-vectors of width 8 612 | 613 | Running AFA-based consistency check 614 | Using straight-line solver 615 | After splitting and topological sorting: 4 AFAs 616 | After treeification: 2 trees 617 | Final AFA has 26 states ... is non-empty! 618 | sat 619 | 620 | (define-fun x_10 () String "hallo") 621 | (define-fun x_11 () String "ha") 622 | (define-fun x_12 () String "llo") 623 | (define-fun x_7 () String "hallo") 624 | 625 | simple-replace-4b.smt2 626 | Reading file simple-replace-4b.smt2 ... 627 | Warning: ignoring option :strings-exp 628 | Warning: get-model detected, producing model 629 | Assuming bit-vectors of width 8 630 | 631 | Running AFA-based consistency check 632 | Using straight-line solver 633 | After splitting and topological sorting: 4 AFAs 634 | After treeification: 2 trees 635 | Final AFA has 26 states ... is empty! 636 | unsat 637 | 638 | simple-replace-4c.smt2 639 | Reading file simple-replace-4c.smt2 ... 640 | Warning: ignoring option :strings-exp 641 | Warning: get-model detected, producing model 642 | Assuming bit-vectors of width 8 643 | 644 | Running AFA-based consistency check 645 | Using straight-line solver 646 | After splitting and topological sorting: 2 AFAs 647 | After treeification: 2 trees 648 | Final AFA has 14 states ... is non-empty! 649 | sat 650 | 651 | (define-fun x_10 () String "") 652 | (define-fun x_11 () String "") 653 | (define-fun x_12 () String "") 654 | (define-fun x_7 () String "") 655 | 656 | extract-1.smt2 657 | Reading file extract-1.smt2 ... 658 | Parsing transducer (2 tracks): 659 | 660 | State extract1st: 661 | (accepting) 662 | -> extract1st: seq_head(_1) != 61.\as[bv[8]] 663 | -> extract1st_2: seq_head(_1) = 61.\as[bv[8]] 664 | 665 | State extract1st_2: 666 | (accepting) 667 | -> extract1st_2: seq_head(_1) = seq_head(_0) & seq_head(_1) != 61.\as[bv[8]] 668 | -> extract1st_3: seq_head(_1) = 61.\as[bv[8]] 669 | 670 | State extract1st_3: 671 | (accepting) 672 | -> extract1st_3: true 673 | 674 | Assuming bit-vectors of width 8 675 | 676 | Running AFA-based consistency check 677 | Using straight-line solver 678 | After splitting and topological sorting: 4 AFAs 679 | After treeification: 2 trees 680 | Final AFA has 22 states ... is empty! 681 | unsat 682 | 683 | extract-1b.smt2 684 | Reading file extract-1b.smt2 ... 685 | Parsing transducer (2 tracks): 686 | 687 | State extract1st: 688 | (accepting) 689 | -> extract1st: seq_head(_1) != 61.\as[bv[8]] 690 | -> extract1st_2: seq_head(_1) = 61.\as[bv[8]] 691 | 692 | State extract1st_2: 693 | (accepting) 694 | -> extract1st_2: seq_head(_1) = seq_head(_0) & seq_head(_1) != 61.\as[bv[8]] 695 | -> extract1st_3: seq_head(_1) = 61.\as[bv[8]] 696 | 697 | State extract1st_3: 698 | (accepting) 699 | -> extract1st_3: true 700 | 701 | Assuming bit-vectors of width 8 702 | 703 | Running AFA-based consistency check 704 | Using straight-line solver 705 | After splitting and topological sorting: 5 AFAs 706 | After treeification: 2 trees 707 | Final AFA has 9 states ... is empty! 708 | unsat 709 | 710 | empty-concat.smt2 711 | Reading file empty-concat.smt2 ... 712 | Warning: get-model detected, producing model 713 | Assuming bit-vectors of width 8 714 | 715 | Running AFA-based consistency check 716 | Using straight-line solver 717 | After splitting and topological sorting: 0 AFAs 718 | After treeification: 0 trees 719 | sat 720 | 721 | (define-fun literal_1 () String "") 722 | (define-fun sigmaStar_0 () String "") 723 | (define-fun x_2 () String "") 724 | 725 | escapeSequences-1a.smt2 726 | Reading file escapeSequences-1a.smt2 ... 727 | Assuming bit-vectors of width 8 728 | 729 | Running AFA-based consistency check 730 | Using tree-based solver 731 | Solving constraints: 732 | Left(P4) 733 | Right(member(P4, P6)) 734 | 735 | Resulting AFA over variables P4 has 7 states ... is empty! 736 | unsat 737 | 738 | escapeSequences-1b.smt2 739 | Reading file escapeSequences-1b.smt2 ... 740 | Assuming bit-vectors of width 8 741 | 742 | Running AFA-based consistency check 743 | Using tree-based solver 744 | Solving constraints: 745 | Left(P4) 746 | Right(member(P4, P6)) 747 | 748 | Resulting AFA over variables P4 has 7 states ... is non-empty! 749 | sat 750 | 751 | (define-fun x () String "hello") 752 | 753 | epsilon-1.smt2 754 | Reading file epsilon-1.smt2 ... 755 | Warning: ignoring option :strings-exp 756 | Warning: get-model detected, producing model 757 | Assuming bit-vectors of width 8 758 | 759 | Running AFA-based consistency check 760 | Using straight-line solver 761 | After splitting and topological sorting: 1 AFAs 762 | After treeification: 1 trees 763 | Final AFA has 7 states ... is empty! 764 | Running AFA-based consistency check 765 | Using straight-line solver 766 | After splitting and topological sorting: 3 AFAs 767 | After treeification: 3 trees 768 | Final AFA has 26 states ... is non-empty! 769 | sat 770 | 771 | (define-fun epsilon () String "") 772 | (define-fun literal_13 () String ".gif") 773 | (define-fun literal_16 () String "//") 774 | (define-fun literal_9 () String "Large ") 775 | (define-fun sigmaStar_12 () String "evil") 776 | (define-fun sigmaStar_7 () String "") 777 | (define-fun x_10 () String "Large ") 778 | (define-fun x_14 () String "evil.gif") 779 | (define-fun x_17 () String "//evil.gif") 780 | (define-fun x_18 () String "//evil.gif") 781 | (define-fun x_8 () String "") 782 | 783 | epsilon-2.smt2 784 | Reading file epsilon-2.smt2 ... 785 | Warning: ignoring option :strings-exp 786 | Warning: get-model detected, producing model 787 | Assuming bit-vectors of width 8 788 | 789 | Running AFA-based consistency check 790 | Using tree-based solver 791 | Solving constraints: 792 | Left(P0) 793 | Right(replace(P0, P7, P12, P13)) 794 | Left(P13) 795 | Right(member(P13, P23)) 796 | 797 | Resulting AFA over variables P0, P13 has 32 states ... is empty! 798 | unsat 799 | --------------------------------------------------------------------------------