From 4207c1e70c5ccf0dfb63b3ff3523672a2a80708f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Mayer?= <a-mikmay@microsoft.com> Date: Mon, 26 Oct 2015 14:50:07 +0100 Subject: [PATCH] Added string support in smtlib interface Added more documentation --- build.sbt | 2 +- .../leon/solvers/EnumerationSolver.scala | 2 ++ src/main/scala/leon/solvers/Solver.scala | 2 ++ .../leon/solvers/smtlib/SMTLIBTarget.scala | 36 +++++++++++++++++++ .../scala/leon/utils/IncrementalSet.scala | 12 ++++++- 5 files changed, 52 insertions(+), 2 deletions(-) diff --git a/build.sbt b/build.sbt index 9215fea00..5c7ac9891 100644 --- a/build.sbt +++ b/build.sbt @@ -143,7 +143,7 @@ def ghProject(repo: String, version: String) = RootProject(uri(s"${repo}#${versi lazy val bonsai = ghProject("git://github.com/colder/bonsai.git", "0fec9f97f4220fa94b1f3f305f2e8b76a3cd1539") -lazy val scalaSmtLib = ghProject("git://github.com/regb/scala-smtlib.git", "3b6ef4992b6af15d08a7320fd12202f35e97b905") +lazy val scalaSmtLib = ghProject("git://github.com/MikaelMayer/scala-smtlib.git", "8ef13ef3294ab823aed0bad40678f507b1fe63e2") lazy val root = (project in file(".")). configs(RegressionTest, IsabelleTest, IntegrTest). diff --git a/src/main/scala/leon/solvers/EnumerationSolver.scala b/src/main/scala/leon/solvers/EnumerationSolver.scala index d7d52d371..a9bbe8b45 100644 --- a/src/main/scala/leon/solvers/EnumerationSolver.scala +++ b/src/main/scala/leon/solvers/EnumerationSolver.scala @@ -48,6 +48,7 @@ class EnumerationSolver(val context: LeonContext, val program: Program) extends private var model = Model.empty + /** @inheritdoc */ def check: Option[Boolean] = { val timer = context.timers.solvers.enum.check.start() val res = try { @@ -78,6 +79,7 @@ class EnumerationSolver(val context: LeonContext, val program: Program) extends res } + /** @inheritdoc */ def getModel: Model = { model } diff --git a/src/main/scala/leon/solvers/Solver.scala b/src/main/scala/leon/solvers/Solver.scala index 10902ebbf..85b196a3a 100644 --- a/src/main/scala/leon/solvers/Solver.scala +++ b/src/main/scala/leon/solvers/Solver.scala @@ -23,7 +23,9 @@ trait Solver extends Interruptible { assertCnstr(Not(vc.condition)) } + /** Returns Some(true) if it found a satisfying model, Some(false) if no model exists, and None otherwise */ def check: Option[Boolean] + /** Returns the model if it exists */ def getModel: Model def getResultSolver: Option[Solver] = Some(this) diff --git a/src/main/scala/leon/solvers/smtlib/SMTLIBTarget.scala b/src/main/scala/leon/solvers/smtlib/SMTLIBTarget.scala index 01b35d9de..5d11ec592 100644 --- a/src/main/scala/leon/solvers/smtlib/SMTLIBTarget.scala +++ b/src/main/scala/leon/solvers/smtlib/SMTLIBTarget.scala @@ -26,6 +26,7 @@ import _root_.smtlib.parser.Terms.{ } import _root_.smtlib.parser.CommandsResponses.{ Error => ErrorResponse, _ } import _root_.smtlib.theories._ +import _root_.smtlib.theories.experimental._ import _root_.smtlib.interpreters.ProcessInterpreter trait SMTLIBTarget extends Interruptible { @@ -233,6 +234,7 @@ trait SMTLIBTarget extends Interruptible { case RealType => Reals.RealSort() case Int32Type => FixedSizeBitVectors.BitVectorSort(32) case CharType => FixedSizeBitVectors.BitVectorSort(32) + case StringType => Strings.StringSort() case RawArrayType(from, to) => Sort(SMTIdentifier(SSymbol("Array")), Seq(declareSort(from), declareSort(to))) @@ -356,6 +358,9 @@ trait SMTLIBTarget extends Interruptible { case FractionalLiteral(n, d) => Reals.Div(Reals.NumeralLit(n), Reals.NumeralLit(d)) case CharLiteral(c) => FixedSizeBitVectors.BitVectorLit(Hexadecimal.fromInt(c.toInt)) case BooleanLiteral(v) => Core.BoolConst(v) + case StringLiteral(v) => + declareSort(StringType) + Strings.StringLit(v) case Let(b, d, e) => val id = id2sym(b) val value = toSMT(d) @@ -586,6 +591,12 @@ trait SMTLIBTarget extends Interruptible { case RealMinus(a, b) => Reals.Sub(toSMT(a), toSMT(b)) case RealTimes(a, b) => Reals.Mul(toSMT(a), toSMT(b)) case RealDivision(a, b) => Reals.Div(toSMT(a), toSMT(b)) + + case StringLength(a) => Strings.Length(toSMT(a)) + case StringConcat(a, b) => Strings.Concat(toSMT(a), toSMT(b)) + case SubString(a, start, Plus(start2, length)) if start == start2 => + Strings.Substring(toSMT(a),toSMT(start),toSMT(length)) + case SubString(a, start, end) => Strings.Substring(toSMT(a),toSMT(start),toSMT(Minus(end, start))) case And(sub) => Core.And(sub.map(toSMT): _*) case Or(sub) => Core.Or(sub.map(toSMT): _*) @@ -641,6 +652,31 @@ trait SMTLIBTarget extends Interruptible { case (SNumeral(n), Some(RealType)) => FractionalLiteral(n, 1) + + case (SString(v), Some(StringType)) => + StringLiteral(v) + + case (Strings.Length(a), _) => + val aa = fromSMT(a) + StringLength(aa) + + case (Strings.Concat(a, b, c @ _*), _) => + val aa = fromSMT(a) + val bb = fromSMT(b) + (StringConcat(aa, bb) /: c.map(fromSMT(_))) { + case (s, cc) => StringConcat(s, cc) + } + + case (Strings.Substring(s, start, offset), _) => + val ss = fromSMT(s) + val tt = fromSMT(start) + val oo = fromSMT(offset) + oo match { + case Minus(otherEnd, `tt`) => SubString(ss, tt, otherEnd) + case _ => SubString(ss, tt, Plus(tt, oo)) + } + + case (Strings.At(a, b), _) => fromSMT(Strings.Substring(a, b, SNumeral(1))) case (FunctionApplication(SimpleSymbol(SSymbol("ite")), Seq(cond, thenn, elze)), t) => IfExpr( diff --git a/src/main/scala/leon/utils/IncrementalSet.scala b/src/main/scala/leon/utils/IncrementalSet.scala index 163da296c..311e55096 100644 --- a/src/main/scala/leon/utils/IncrementalSet.scala +++ b/src/main/scala/leon/utils/IncrementalSet.scala @@ -6,6 +6,7 @@ import scala.collection.mutable.{Stack, Set => MSet} import scala.collection.mutable.Builder import scala.collection.{Iterable, IterableLike, GenSet} +/** A stack of mutable sets with a set-like API and methods to push and pop */ class IncrementalSet[A] extends IncrementalState with Iterable[A] with IterableLike[A, Set[A]] @@ -14,32 +15,41 @@ class IncrementalSet[A] extends IncrementalState private[this] val stack = new Stack[MSet[A]]() override def repr = stack.flatten.toSet + /** Removes all the elements */ override def clear(): Unit = { stack.clear() } + /** Removes all the elements and creates a new set */ def reset(): Unit = { clear() push() } + /** Creates one more set level */ def push(): Unit = { stack.push(MSet()) } + /** Removes one set level */ def pop(): Unit = { stack.pop() } + /** Returns true if the set contains elem */ def apply(elem: A) = repr.contains(elem) + /** Returns true if the set contains elem */ def contains(elem: A) = repr.contains(elem) + /** Returns an iterator over all the elements */ def iterator = stack.flatten.iterator + /** Add an element to the head set */ def += (elem: A) = { stack.head += elem; this } + /** Removes an element from all stacked sets */ def -= (elem: A) = { stack.foreach(_ -= elem); this } override def newBuilder = new scala.collection.mutable.SetBuilder(Set.empty[A]) def result = this - push() + push() // By default, creates a new empty mutable set ready to add elements to it. } -- GitLab