From 3b63c054620eadc28c529c1dccc329ca26c95ae8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mika=C3=ABl=20Mayer?= <mikael.mayer@epfl.ch>
Date: Mon, 9 May 2016 19:30:12 +0200
Subject: [PATCH] SelfPrettyPrinter can now return functions with free
 variables.

---
 .../leon/purescala/SelfPrettyPrinter.scala    | 139 +++++++++++++-----
 1 file changed, 102 insertions(+), 37 deletions(-)

diff --git a/src/main/scala/leon/purescala/SelfPrettyPrinter.scala b/src/main/scala/leon/purescala/SelfPrettyPrinter.scala
index 9212163a7..bba231525 100644
--- a/src/main/scala/leon/purescala/SelfPrettyPrinter.scala
+++ b/src/main/scala/leon/purescala/SelfPrettyPrinter.scala
@@ -33,64 +33,129 @@ object SelfPrettyPrinter {
   }
 }
 
-/** This pretty-printer uses functions defined in Leon itself.
-  * If not pretty printing function is defined, return the default value instead
-  */
-class SelfPrettyPrinter {
-  implicit val section = leon.utils.DebugSectionEvaluation
-  private var allowedFunctions = Set[FunDef]()
-  private var excluded = Set[FunDef]()
-  /** Functions whose name does not need to end with `tostring` or which can be abstract, i.e. which may contain a choose construct.*/
-  def allowFunction(fd: FunDef) = { allowedFunctions += fd; this }
+/** T is the type of pretty-printers which have to be found (e.g. Lambda or Lambdas with identifiers)
+ *  U is the type of the arguments during gathering */
+trait PrettyPrinterFinder[T, U >: T] {
+  protected def isExcluded(fd: FunDef): Boolean
+  protected def isAllowed(fd: FunDef): Boolean
   
-  def excludeFunctions(fds: Set[FunDef]) = { excluded ++= fds; this }
-  def excludeFunction(fd: FunDef) = { excluded += fd; this }
+  @inline def isValidPrinterName(s: String) = { val n = s.toLowerCase(); n.endsWith("tostring") || n.endsWith("mkstring") }
+  
+  @inline def isCandidate(fd: FunDef) = fd.returnType == StringType &&
+        fd.params.nonEmpty &&
+        !isExcluded(fd) &&
+        (isAllowed(fd) || isValidPrinterName(fd.id.name))
 
   /** Returns a list of possible lambdas that can transform the input type to a String.
     * At this point, it does not consider yet the inputType. Only [[prettyPrinterFromCandidate]] will consider it. */
-  def prettyPrintersForType(inputType: TypeTree/*, existingPp: Map[TypeTree, List[Lambda]] = Map()*/)(implicit ctx: LeonContext, program: Program): Stream[Lambda] = {
+  def prettyPrintersForType(inputType: TypeTree/*, existingPp: Map[TypeTree, List[Lambda]] = Map()*/)(implicit ctx: LeonContext, program: Program): Stream[T] = {
     program.definedFunctions.toStream flatMap { fd =>
-      val isCandidate =
-        fd.returnType == StringType &&
-        fd.params.nonEmpty &&
-        !excluded(fd) &&
-        (allowedFunctions(fd) || fd.id.name.toLowerCase().endsWith("tostring"))
-
-      if(isCandidate) {
-        prettyPrinterFromCandidate(fd, inputType)
-      } else {
-        Stream.Empty
-      }
+      if(isCandidate(fd)) prettyPrinterFromCandidate(fd, inputType) else Stream.Empty
     }
   }
   
+  def getPrintersForType(t: TypeTree)(implicit ctx: LeonContext, program: Program): Option[Stream[U]] = t match {
+    case FunctionType(Seq(in), StringType) => // Should have one argument.
+      Some(prettyPrintersForType(in))
+    case _ => None
+  }
+  
+  // To Implement
+  def buildLambda(inputType: TypeTree, fd: FunDef, slu: Stream[List[U]]): Stream[T]
   
-  def prettyPrinterFromCandidate(fd: FunDef, inputType: TypeTree)(implicit ctx: LeonContext, program: Program): Stream[Lambda] = {
+  def prettyPrinterFromCandidate(fd: FunDef, inputType: TypeTree)(implicit ctx: LeonContext, program: Program): Stream[T] = {
+    println("Considering pretty printer " + fd.id.name + " with arg " + fd.params.head.getType + " for type " + inputType)
     TypeOps.canBeSubtypeOf(inputType, fd.tparams.map(_.tp), fd.params.head.getType) match {
-      case Some(genericTypeMap) => 
+      case Some(genericTypeMap) =>
+        println("Found a mapping")
         val defGenericTypeMap = genericTypeMap.map{ case (k, v) => (Definitions.TypeParameterDef(k), v) }
-        def gatherPrettyPrinters(funIds: List[Identifier], acc: ListBuffer[Stream[Lambda]] = ListBuffer()): Option[Stream[List[Lambda]]] = funIds match {
+        def gatherPrettyPrinters(funIds: List[Identifier], acc: ListBuffer[Stream[U]] = ListBuffer[Stream[U]]()): Option[Stream[List[U]]] = funIds match {
           case Nil => Some(StreamUtils.cartesianProduct(acc.toList))
           case funId::tail => // For each function, find an expression which could be provided if it exists.
-            funId.getType match {
-              case FunctionType(Seq(in), StringType) => // Should have one argument.
-                val candidates = prettyPrintersForType(in)
-                gatherPrettyPrinters(tail, acc += candidates)
-              case _ => None
+            getPrintersForType(funId.getType) match {
+              case Some(u) => gatherPrettyPrinters(tail, acc += u)
+              case None    =>
+                println("could not finish")
+                None
             }
         }
         val funIds = fd.params.tail.map(x => TypeOps.instantiateType(x.id, defGenericTypeMap)).toList
         gatherPrettyPrinters(funIds) match {
-          case Some(l) => for(lambdas <- l) yield {
-            val x = FreshIdentifier("x", inputType) // verify the type
-            Lambda(Seq(ValDef(x)), functionInvocation(fd, Variable(x)::lambdas))
-          }
-          case _ => Stream.empty
+          case Some(l) => buildLambda(inputType, fd, l)
+          case None =>    Stream.empty
         }
-      case None => Stream.empty
+      case None =>        Stream.empty
     }
   }
+}
+
+/** This pretty-printer uses functions defined in Leon itself.
+  * If not pretty printing function is defined, return the default value instead
+  */
+class SelfPrettyPrinter extends PrettyPrinterFinder[Lambda, Lambda] { top =>
+  implicit val section = leon.utils.DebugSectionEvaluation
+  /* Functions whose name does not need to end with `tostring` or which can be abstract, i.e. which may contain a choose construct.*/
+  protected var allowedFunctions = Set[FunDef]()
+  /* Functions totally excluded from the set of pretty-printer candidates */
+  protected var excluded = Set[FunDef]()
+  /** Functions whose name does not need to end with `tostring` or which can be abstract, i.e. which may contain a choose construct.*/
+  def allowFunction(fd: FunDef) = { allowedFunctions += fd; this }
+  /** Functions totally excluded from the set of pretty-printer candidates*/
+  def excludeFunctions(fds: Set[FunDef]) = { excluded ++= fds; this }
+  /** Function totally excluded from the set of pretty-printer candidates*/
+  def excludeFunction(fd: FunDef) = { excluded += fd; this }
 
+  protected def isExcluded(fd: FunDef): Boolean = top.excluded(fd)
+  protected def isAllowed(fd: FunDef): Boolean = top.allowedFunctions(fd)
+  
+  override def getPrintersForType(t: TypeTree)(implicit ctx: LeonContext, program: Program): Option[Stream[Lambda]] = t match {
+    case FunctionType(Seq(StringType), StringType) => // Should have one argument.
+      val s = FreshIdentifier("s", StringType) // verify the type
+      Some(Stream(Lambda(Seq(ValDef(s)), Variable(s))) ++ super.getPrintersForType(t).getOrElse(Stream.empty) )
+    case _ => super.getPrintersForType(t)
+  }
+  
+  /** From a list of lambdas used for pretty-printing the arguments of a function, builds the lambda for the function itself. */
+  def buildLambda(inputType: TypeTree, fd: FunDef, slu: Stream[List[Lambda]]): Stream[Lambda] = {
+    for(lambdas <- slu) yield {
+      val x = FreshIdentifier("x", inputType) // verify the type
+      Lambda(Seq(ValDef(x)), functionInvocation(fd, Variable(x)::lambdas))
+    }
+  }
+  
+  object withPossibleParameters extends PrettyPrinterFinder[(Lambda, List[Identifier]), (Expr, List[Identifier])]  {
+    protected def isExcluded(fd: FunDef): Boolean = top.excluded(fd)
+    protected def isAllowed(fd: FunDef): Boolean = top.allowedFunctions(fd)
+    
+    /** If the returned identifiers are instantiated, each lambda becomes a pretty-printer.
+      * This allows to make use of mkString functions such as for maps */
+    def prettyPrintersForTypes(inputType: TypeTree)(implicit ctx: LeonContext, program: Program) = {
+      program.definedFunctions.toStream flatMap { fd =>
+        if(isCandidate(fd)) prettyPrinterFromCandidate(fd, inputType) else Stream.Empty
+      }
+    }
+    
+    /** Adds the possibility to have holes in expression */
+    override def getPrintersForType(t: TypeTree)(implicit ctx: LeonContext, program: Program) = t match {
+      case FunctionType(Seq(StringType), StringType) => // Should have one argument.
+        val s = FreshIdentifier("s", StringType) // verify the type
+        Some(Stream((Lambda(Seq(ValDef(s)), Variable(s)), List())) ++ super.getPrintersForType(t).getOrElse(Stream.empty) )
+      case StringType => 
+        val const = FreshIdentifier("const", StringType)
+        Some(Stream((Variable(const), List(const))))
+      case _ => super.getPrintersForType(t)
+    }
+    
+    /** From a list of expressions gathered for the parameters of the function definition, builds the lambda. */
+    def buildLambda(inputType: TypeTree, fd: FunDef, slu: Stream[List[(Expr, List[Identifier])]]) = {
+      for(lambdas <- slu) yield {
+        val (args, ids) = lambdas.unzip
+        val all_ids = ids.flatten
+        val x = FreshIdentifier("x", inputType) // verify the type
+        (Lambda(Seq(ValDef(x)), functionInvocation(fd, Variable(x)::args)), all_ids)
+      }
+    }
+  }
 
   /** Actually prints the expression with as alternative the given orElse
     * @param excluded The list of functions which should be excluded from pretty-printing
-- 
GitLab