diff --git a/src/org/sosy_lab/java_smt/api/StringFormulaManager.java b/src/org/sosy_lab/java_smt/api/StringFormulaManager.java
index 4ba3235f86..ae25341148 100644
--- a/src/org/sosy_lab/java_smt/api/StringFormulaManager.java
+++ b/src/org/sosy_lab/java_smt/api/StringFormulaManager.java
@@ -8,6 +8,7 @@
package org.sosy_lab.java_smt.api;
+import com.google.common.base.Preconditions;
import java.util.Arrays;
import java.util.List;
import org.sosy_lab.java_smt.api.NumeralFormula.IntegerFormula;
@@ -124,10 +125,20 @@ default StringFormula concat(StringFormula... parts) {
RegexFormula none();
/**
+ * Note: The size of the used alphabet depends on the underlying SMT solver.
+ *
* @return formula denoting the set of all strings, also known as Regex ".*"
.
*/
RegexFormula all();
+ /**
+ * Note: The size of the used alphabet depends on the underlying SMT solver.
+ *
+ * @return formula denoting the set of all strings of length 1, also known as DOT operator which
+ * represents one arbitrary char, or as Regex "."
.
+ */
+ RegexFormula allChar();
+
/**
* @return formula denoting the range regular expression over two sequences of length 1.
*/
@@ -138,6 +149,13 @@ default StringFormula concat(StringFormula... parts) {
* @see #range(StringFormula, StringFormula)
*/
default RegexFormula range(char start, char end) {
+ Preconditions.checkArgument(
+ start <= end,
+ "Range from start '%s' (%s) to end '%s' (%s) is empty.",
+ start,
+ (int) start,
+ end,
+ (int) end);
return range(makeString(String.valueOf(start)), makeString(String.valueOf(end)));
}
diff --git a/src/org/sosy_lab/java_smt/basicimpl/AbstractStringFormulaManager.java b/src/org/sosy_lab/java_smt/basicimpl/AbstractStringFormulaManager.java
index 12efe42c95..2b3b87915b 100644
--- a/src/org/sosy_lab/java_smt/basicimpl/AbstractStringFormulaManager.java
+++ b/src/org/sosy_lab/java_smt/basicimpl/AbstractStringFormulaManager.java
@@ -205,6 +205,13 @@ public RegexFormula all() {
protected abstract TFormulaInfo allImpl();
+ @Override
+ public RegexFormula allChar() {
+ return wrapRegex(allCharImpl());
+ }
+
+ protected abstract TFormulaInfo allCharImpl();
+
@Override
public RegexFormula range(StringFormula start, StringFormula end) {
return wrapRegex(range(extractInfo(start), extractInfo(end)));
diff --git a/src/org/sosy_lab/java_smt/delegate/statistics/StatisticsStringFormulaManager.java b/src/org/sosy_lab/java_smt/delegate/statistics/StatisticsStringFormulaManager.java
index b47a38a72d..3d2032abb6 100644
--- a/src/org/sosy_lab/java_smt/delegate/statistics/StatisticsStringFormulaManager.java
+++ b/src/org/sosy_lab/java_smt/delegate/statistics/StatisticsStringFormulaManager.java
@@ -156,6 +156,12 @@ public RegexFormula all() {
return delegate.all();
}
+ @Override
+ public RegexFormula allChar() {
+ stats.stringOperations.getAndIncrement();
+ return delegate.allChar();
+ }
+
@Override
public RegexFormula range(StringFormula start, StringFormula end) {
stats.stringOperations.getAndIncrement();
diff --git a/src/org/sosy_lab/java_smt/delegate/synchronize/SynchronizedStringFormulaManager.java b/src/org/sosy_lab/java_smt/delegate/synchronize/SynchronizedStringFormulaManager.java
index 5c3a0b807a..c41e991eab 100644
--- a/src/org/sosy_lab/java_smt/delegate/synchronize/SynchronizedStringFormulaManager.java
+++ b/src/org/sosy_lab/java_smt/delegate/synchronize/SynchronizedStringFormulaManager.java
@@ -178,6 +178,13 @@ public RegexFormula all() {
}
}
+ @Override
+ public RegexFormula allChar() {
+ synchronized (sync) {
+ return delegate.allChar();
+ }
+ }
+
@Override
public RegexFormula range(StringFormula start, StringFormula end) {
synchronized (sync) {
diff --git a/src/org/sosy_lab/java_smt/solvers/cvc4/CVC4StringFormulaManager.java b/src/org/sosy_lab/java_smt/solvers/cvc4/CVC4StringFormulaManager.java
index c9cb49a46f..5701157f3f 100644
--- a/src/org/sosy_lab/java_smt/solvers/cvc4/CVC4StringFormulaManager.java
+++ b/src/org/sosy_lab/java_smt/solvers/cvc4/CVC4StringFormulaManager.java
@@ -138,6 +138,11 @@ protected Expr allImpl() {
return exprManager.mkExpr(Kind.REGEXP_COMPLEMENT, noneImpl());
}
+ @Override
+ protected Expr allCharImpl() {
+ return exprManager.mkExpr(Kind.REGEXP_SIGMA, new vectorExpr());
+ }
+
@Override
protected Expr range(Expr start, Expr end) {
return exprManager.mkExpr(Kind.REGEXP_RANGE, start, end);
diff --git a/src/org/sosy_lab/java_smt/solvers/z3/Z3StringFormulaManager.java b/src/org/sosy_lab/java_smt/solvers/z3/Z3StringFormulaManager.java
index 4793e6f95f..61765ea531 100644
--- a/src/org/sosy_lab/java_smt/solvers/z3/Z3StringFormulaManager.java
+++ b/src/org/sosy_lab/java_smt/solvers/z3/Z3StringFormulaManager.java
@@ -131,6 +131,11 @@ protected Long allImpl() {
return Native.mkReFull(z3context, formulaCreator.getRegexType());
}
+ @Override
+ protected Long allCharImpl() {
+ return Native.mkReAllchar(z3context, formulaCreator.getRegexType());
+ }
+
@Override
protected Long range(Long start, Long end) {
return Native.mkReRange(z3context, start, end);
diff --git a/src/org/sosy_lab/java_smt/test/StringFormulaManagerTest.java b/src/org/sosy_lab/java_smt/test/StringFormulaManagerTest.java
index b396059949..6a0f946ae6 100644
--- a/src/org/sosy_lab/java_smt/test/StringFormulaManagerTest.java
+++ b/src/org/sosy_lab/java_smt/test/StringFormulaManagerTest.java
@@ -128,6 +128,47 @@ public void testRegexAll3() throws SolverException, InterruptedException {
assertThatFormula(smgr.in(smgr.makeString(".*"), regex)).isSatisfiable();
}
+ @Test
+ public void testRegexAllChar() throws SolverException, InterruptedException {
+ RegexFormula regexAllChar = smgr.allChar();
+
+ assertThatFormula(smgr.in(smgr.makeString("a"), regexAllChar)).isSatisfiable();
+ assertThatFormula(smgr.in(smgr.makeString("ab"), regexAllChar)).isUnsatisfiable();
+ assertThatFormula(smgr.in(smgr.makeString(""), regexAllChar)).isUnsatisfiable();
+ assertThatFormula(smgr.in(smgr.makeString("ab"), smgr.times(regexAllChar, 2))).isSatisfiable();
+ assertThatFormula(
+ smgr.in(smgr.makeVariable("x"), smgr.intersection(smgr.range('9', 'a'), regexAllChar)))
+ .isSatisfiable();
+
+ RegexFormula regexDot = smgr.makeRegex(".");
+ assertThatFormula(smgr.in(smgr.makeString("a"), regexDot)).isUnsatisfiable();
+ }
+
+ @Test
+ public void testRegexAllCharUnicode() throws SolverException, InterruptedException {
+ RegexFormula regexAllChar = smgr.allChar();
+
+ // Single characters.
+ assertThatFormula(smgr.in(smgr.makeString("\\u0394"), regexAllChar)).isSatisfiable();
+ assertThatFormula(smgr.in(smgr.makeString("\\u{1fa6a}"), regexAllChar)).isSatisfiable();
+
+ // Combining characters are not matched as one character.
+ assertThatFormula(smgr.in(smgr.makeString("a\\u0336"), regexAllChar)).isUnsatisfiable();
+ assertThatFormula(smgr.in(smgr.makeString("\\n"), regexAllChar)).isUnsatisfiable();
+
+ if (solverUnderTest != Solvers.CVC4) {
+ // CVC4 does not support Unicode characters.
+ // Z3 and other solvers support Unicode characters in the theory of strings.
+ assertThatFormula(
+ smgr.in(
+ smgr.makeVariable("x"), smgr.intersection(smgr.range('a', 'Δ'), regexAllChar)))
+ .isSatisfiable();
+ // Combining characters are not matched as one character.
+ // Non-ascii non-printable characters should use the codepoint representation
+ assertThatFormula(smgr.in(smgr.makeString("Δ"), regexAllChar)).isUnsatisfiable();
+ }
+ }
+
@Test
public void testStringRegex2() throws SolverException, InterruptedException {
RegexFormula regex = smgr.concat(smgr.closure(a2z), smgr.makeRegex("ll"), smgr.closure(a2z));