From 902b1adbc9a52d9eaab1b84bda690fb78294b0dd Mon Sep 17 00:00:00 2001
From: Etienne Kneuss <colder@php.net>
Date: Tue, 6 May 2014 14:25:53 +0200
Subject: [PATCH] Fix printing bugs, add synthesis->print tests

---
 .../scala/leon/purescala/PrettyPrinter.scala  |  26 +++-
 .../scala/leon/purescala/ScalaPrinter.scala   |  12 +-
 src/main/scala/leon/purescala/TreeOps.scala   |   6 +-
 .../scala/leon/synthesis/FileInterface.scala  |   2 +-
 .../scala/leon/synthesis/Heuristics.scala     |   2 +-
 src/main/scala/leon/synthesis/Solution.scala  |   2 +-
 .../test/synthesis/StablePrintingSuite.scala  | 118 ++++++++++++++++++
 7 files changed, 156 insertions(+), 12 deletions(-)
 create mode 100644 src/test/scala/leon/test/synthesis/StablePrintingSuite.scala

diff --git a/src/main/scala/leon/purescala/PrettyPrinter.scala b/src/main/scala/leon/purescala/PrettyPrinter.scala
index fb8831f64..85be66b6b 100644
--- a/src/main/scala/leon/purescala/PrettyPrinter.scala
+++ b/src/main/scala/leon/purescala/PrettyPrinter.scala
@@ -191,8 +191,14 @@ class PrettyPrinter(opts: PrinterOptions, val sb: StringBuffer = new StringBuffe
       case Tuple(exprs)         => p"($exprs)"
       case TupleSelect(t, i)    => p"${t}._$i"
       case h @ Hole(o)          => p"???[${h.getType}]($o)"
-      case Choose(vars, pred)   => p"choose($vars => $pred)"
-      case CaseClassInstanceOf(cct, e)         => p"$e.isInstanceOf[$cct]"
+      case Choose(vars, pred)   => p"choose(($vars) => $pred)"
+      case e @ Error(err)       => p"""error[${e.getType}]("$err")"""
+      case CaseClassInstanceOf(cct, e)         =>
+        if (cct.classDef.isCaseObject) {
+          p"($e == ${cct.id})"
+        } else {
+          p"$e.isInstanceOf[$cct]"
+        }
       case CaseClassSelector(_, e, id)         => p"$e.$id"
       case MethodInvocation(rec, _, tfd, args) =>
         p"$rec.${tfd.id}"
@@ -293,11 +299,20 @@ class PrettyPrinter(opts: PrinterOptions, val sb: StringBuffer = new StringBuffe
 
       case CaseClassPattern(ob, cct, subps) =>
         ob.foreach { b => p"$b @ " }
-        p"${cct.id}($subps)"
+        if (cct.classDef.isCaseObject) {
+          p"${cct.id}"
+        } else {
+          p"${cct.id}($subps)"
+        }
 
       case InstanceOfPattern(ob, cct) =>
-        ob.foreach { b => p"$b : " }
-        p"$cct"
+        if (cct.classDef.isCaseObject) {
+          ob.foreach { b => p"$b @ " }
+          p"${cct.id}"
+        } else {
+          ob.foreach { b => p"$b : " }
+          p"$cct"
+        }
 
       case TuplePattern(ob, subps) =>
         ob.foreach { b => p"$b @ " }
@@ -420,6 +435,7 @@ class PrettyPrinter(opts: PrinterOptions, val sb: StringBuffer = new StringBuffe
     case (_: Assert, Some(_: Definition)) => true
     case (_, Some(_: Definition)) => false
     case (_, Some(_: MatchExpr | _: MatchCase | _: Let | _: LetTuple | _: LetDef)) => false
+    case (_, Some(_: MatchExpr | _: MatchCase | _: Let | _: LetTuple | _: LetDef)) => false
     case (_, _) => true
   }
 
diff --git a/src/main/scala/leon/purescala/ScalaPrinter.scala b/src/main/scala/leon/purescala/ScalaPrinter.scala
index 755e48af7..87090a80d 100644
--- a/src/main/scala/leon/purescala/ScalaPrinter.scala
+++ b/src/main/scala/leon/purescala/ScalaPrinter.scala
@@ -16,9 +16,17 @@ class ScalaPrinter(opts: PrinterOptions, sb: StringBuffer = new StringBuffer) ex
   override def pp(tree: Tree)(implicit ctx: PrinterContext): Unit = {
     tree match {
       case Not(Equals(l, r))    => p"$l != $r"
-      case Iff(l,r)             => p"$l == $r"
+      case Iff(l,r)             => pp(Equals(l, r))
       case Implies(l,r)         => pp(Or(Not(l), r))
-      case Choose(vars, pred)   => p"choose(${typed(vars)} => $pred)" // TODO
+      case Choose(vars, pred)   => p"choose((${typed(vars)}) => $pred)"
+      case s @ FiniteSet(rs)    => {
+        s.getType match {
+          case SetType(ut) =>
+            p"Set[$ut]($rs)"
+          case _ =>
+            p"Set($rs)"
+        }
+      }
       case ElementOfSet(e,s)    => p"$s.contains(e)"
       case SetUnion(l,r)        => p"$l ++ $r"
       case MapUnion(l,r)        => p"$l ++ $r"
diff --git a/src/main/scala/leon/purescala/TreeOps.scala b/src/main/scala/leon/purescala/TreeOps.scala
index 68e940449..9bd1b04ed 100644
--- a/src/main/scala/leon/purescala/TreeOps.scala
+++ b/src/main/scala/leon/purescala/TreeOps.scala
@@ -216,11 +216,13 @@ object TreeOps {
   /*
    * Apply 'f' on 'e' as long as until it stays the same (value equality)
    */
-  def fixpoint[T](f: T => T)(e: T): T = {
+  def fixpoint[T](f: T => T, limit: Int = -1)(e: T): T = {
     var v1 = e
     var v2 = f(v1)
-    while(v2 != v1) {
+    var lim = limit
+    while(v2 != v1 && lim != 0) {
       v1 = v2
+      lim -= 1
       v2 = f(v2)
     }
     v2
diff --git a/src/main/scala/leon/synthesis/FileInterface.scala b/src/main/scala/leon/synthesis/FileInterface.scala
index 4936b748c..4874fa838 100644
--- a/src/main/scala/leon/synthesis/FileInterface.scala
+++ b/src/main/scala/leon/synthesis/FileInterface.scala
@@ -64,7 +64,7 @@ class FileInterface(reporter: Reporter) {
         val p = new ScalaPrinter(PrinterOptions())
         p.pp(toTree)(PrinterContext(toTree, Some(fromTree), indent/2, p))
 
-        before + p.toString + after
+        before + "{" + p.toString + "}" + after
 
       case p =>
         sys.error("Substitution requires RangePos on the input tree: "+fromTree +": "+fromTree.getClass+" GOT" +p)
diff --git a/src/main/scala/leon/synthesis/Heuristics.scala b/src/main/scala/leon/synthesis/Heuristics.scala
index 5c3fe9a07..378b0018e 100644
--- a/src/main/scala/leon/synthesis/Heuristics.scala
+++ b/src/main/scala/leon/synthesis/Heuristics.scala
@@ -14,7 +14,7 @@ object Heuristics {
     InnerCaseSplit,
     //new OptimisticInjection(_),
     //new SelectiveInlining(_),
-    ADTLongInduction,
+    //ADTLongInduction,
     ADTInduction
   )
 }
diff --git a/src/main/scala/leon/synthesis/Solution.scala b/src/main/scala/leon/synthesis/Solution.scala
index c504eadac..00a007661 100644
--- a/src/main/scala/leon/synthesis/Solution.scala
+++ b/src/main/scala/leon/synthesis/Solution.scala
@@ -68,7 +68,7 @@ class Solution(val pre: Expr, val defs: Set[FunDef], val term: Expr) {
     }
 
     // Simplify first using stable simplifiers
-    val s = fixpoint(simple)(toExpr)
+    val s = fixpoint(simple, 5)(toExpr)
 
     // Clean up ids/names
     (new ScopeSimplifier).transform(s)
diff --git a/src/test/scala/leon/test/synthesis/StablePrintingSuite.scala b/src/test/scala/leon/test/synthesis/StablePrintingSuite.scala
new file mode 100644
index 000000000..000977b68
--- /dev/null
+++ b/src/test/scala/leon/test/synthesis/StablePrintingSuite.scala
@@ -0,0 +1,118 @@
+/* Copyright 2009-2014 EPFL, Lausanne */
+
+package leon
+package test
+package synthesis
+
+import leon._
+import leon.purescala.Definitions._
+import leon.purescala.Trees._
+import leon.purescala.ScalaPrinter
+import leon.purescala.TreeOps._
+import leon.solvers.z3._
+import leon.solvers.Solver
+import leon.synthesis._
+import leon.synthesis.utils._
+
+import scala.collection.mutable.Stack
+import scala.io.Source
+
+import java.io.{File, BufferedWriter, FileWriter}
+
+class StablePrintingSuite extends LeonTestSuite {
+  private def forEachFileIn(path : String)(block : File => Unit) {
+    val fs = filesInResourceDir(path, _.endsWith(".scala"))
+
+    for(f <- fs) {
+      block(f)
+    }
+  }
+
+
+  private def testIterativeSynthesis(cat: String, f: File, depth: Int) {
+
+    def getChooses(ctx: LeonContext, content: String): (Program, Seq[ChooseInfo]) = {
+      val opts = SynthesisOptions()
+      val pipeline = leon.utils.TemporaryInputPhase andThen 
+                     frontends.scalac.ExtractionPhase andThen
+                     leon.utils.PreprocessingPhase andThen
+                     purescala.FunctionClosure
+
+      val program = pipeline.run(ctx)((content, Nil))
+
+      (program, ChooseInfo.extractFromProgram(ctx, program, opts))
+    }
+
+    case class Job(content: String, choosesToProcess: Set[Int], rules: List[String]) {
+      def info(task: String): String = {
+        val r = if (rules.isEmpty) "<init>" else "after "+rules.head
+
+        val indent = "  "*(rules.size)+" "
+
+        f"${indent+r}%-40s [$task%s]"
+      }
+    }
+
+
+    test(cat+": "+f.getName+" - Synthesis <-> Print (depth="+depth+")") {
+      val res = Source.fromFile(f).mkString
+
+      val workList = Stack[Job](Job(res, Set(), Nil))
+
+      while(!workList.isEmpty) {
+        val reporter = new TestSilentReporter
+        val ctx = createLeonContext("--synthesis").copy(reporter = reporter)
+        val j = workList.pop()
+
+        info(j.info("compilation"))
+
+        val (pgm, chooses) = try {
+          getChooses(ctx, j.content)
+        } catch {
+          case e: Throwable =>
+            val contentWithLines = j.content.split("\n").zipWithIndex.map{ case (l, i) => f"${i+1}%4d: $l"}.mkString("\n")
+            info("Error while compiling:\n"+contentWithLines)
+            for (e <- reporter.lastErrors) {
+              info(e)
+            }
+            fail("Compilation failed")
+        }
+
+        if (j.rules.size < depth) {
+          for ((ci, i) <- chooses.zipWithIndex if j.choosesToProcess(i) || j.choosesToProcess.isEmpty) {
+            val sctx = SynthesisContext.fromSynthesizer(ci.synthesizer)
+            val problem = ci.problem
+            info(j.info("synthesis "+problem))
+            val apps = Rules.getInstantiations(sctx, problem)
+
+            for (a <- apps) {
+              a.apply(sctx) match {
+                case RuleSuccess(sol, isTrusted) =>
+                case RuleDecomposed(sub) =>
+                  a.onSuccess(sub.map(Solution.choose(_))) match {
+                    case Some(sol) =>
+                      val result = sol.toSimplifiedExpr(ctx, pgm)
+                      val newContent = new FileInterface(ctx.reporter).substitute(j.content, ci.ch, result)
+                      workList push Job(newContent, (i to i+sub.size).toSet, a.toString :: j.rules)
+                    case None =>
+                  }
+
+                case RuleApplicationImpossible =>
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+
+
+  forEachFileIn("regression/synthesis/Church/") { f =>
+    testIterativeSynthesis("Church", f, 1)
+  }
+
+  forEachFileIn("regression/synthesis/List/") { f =>
+    testIterativeSynthesis("List", f, 1)
+  }
+}
-- 
GitLab