From d325618d692eea5deabbb1b221ed1c3b593253ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Mayer?= <mikael.mayer@epfl.ch> Date: Mon, 18 Jan 2016 15:57:51 +0100 Subject: [PATCH] The self pretty printer now unifies toString functions to print using generic functions if found. --- .../leon/evaluators/ContextualEvaluator.scala | 2 +- .../leon/purescala/SelfPrettyPrinter.scala | 96 +++++++++++++------ 2 files changed, 69 insertions(+), 29 deletions(-) diff --git a/src/main/scala/leon/evaluators/ContextualEvaluator.scala b/src/main/scala/leon/evaluators/ContextualEvaluator.scala index 0fc33102a..0c23ef39c 100644 --- a/src/main/scala/leon/evaluators/ContextualEvaluator.scala +++ b/src/main/scala/leon/evaluators/ContextualEvaluator.scala @@ -72,7 +72,7 @@ abstract class ContextualEvaluator(ctx: LeonContext, prog: Program, val maxSteps protected def e(expr: Expr)(implicit rctx: RC, gctx: GC): Value - def typeErrorMsg(tree : Expr, expected : TypeTree) : String = s"Type error : expected ${expected.asString}, found ${tree.asString}." + def typeErrorMsg(tree : Expr, expected : TypeTree) : String = s"Type error : expected ${expected.asString}, found ${tree.asString} of type ${tree.getType}." } diff --git a/src/main/scala/leon/purescala/SelfPrettyPrinter.scala b/src/main/scala/leon/purescala/SelfPrettyPrinter.scala index 35e4d1f61..2a2b7307c 100644 --- a/src/main/scala/leon/purescala/SelfPrettyPrinter.scala +++ b/src/main/scala/leon/purescala/SelfPrettyPrinter.scala @@ -2,58 +2,98 @@ package leon.purescala import leon.evaluators.StringTracingEvaluator import leon.purescala -import purescala.Definitions.Program +import leon.solvers.{ HenkinModel, Model, SolverFactory } +import leon.LeonContext +import leon.evaluators +import leon.utils.StreamUtils import leon.evaluators.StringTracingEvaluator -import purescala.Expressions._ -import purescala.Types.StringType +import leon.purescala.Quantification._ import leon.utils.DebugSectionSynthesis import leon.utils.DebugSectionVerification -import leon.purescala.Quantification._ +import purescala.Definitions.Program +import purescala.Expressions._ +import purescala.Types.StringType import purescala.Constructors._ import purescala.ExprOps._ -import purescala.Expressions.{Pattern, Expr} +import purescala.Expressions._ +import purescala.Expressions.{Choose } import purescala.Extractors._ import purescala.TypeOps._ import purescala.Types._ import purescala.Common._ -import purescala.Expressions._ import purescala.Definitions._ -import purescala.SelfPrettyPrinter -import leon.solvers.{ HenkinModel, Model, SolverFactory } -import leon.LeonContext -import leon.evaluators +import scala.collection.mutable.ListBuffer +import leon.evaluators.DefaultEvaluator + /** This pretty-printer uses functions defined in Leon itself. * If not pretty printing function is defined, return the default value instead * @param The list of functions which should be excluded from pretty-printing (to avoid rendering counter-examples of toString methods using the method itself) * @return a user defined string for the given typed expression. */ object SelfPrettyPrinter { - def print(v: Expr, orElse: =>String, excluded: FunDef => Boolean = Set())(implicit ctx: LeonContext, program: Program): String = { - (program.definedFunctions find { - case fd if !excluded(fd) => - fd.returnType == StringType && fd.params.length == 1 && TypeOps.isSubtypeOf(v.getType, fd.params.head.getType) && fd.id.name.toLowerCase().endsWith("tostring") && + /** Returns a list of possible lambdas that can transform the input type to a String*/ + def prettyPrintersForType(inputType: TypeTree/*, existingPp: Map[TypeTree, List[Lambda]] = Map()*/)(implicit ctx: LeonContext, program: Program): Stream[Lambda] = { + // Use the other argument if you need recursive typing (?) + (program.definedFunctions flatMap { + fd => + val isCandidate = fd.returnType == StringType && + fd.params.length >= 1 && + //TypeOps.isSubtypeOf(v.getType, fd.params.head.getType) && + fd.id.name.toLowerCase().endsWith("tostring") && program.callGraph.transitiveCallees(fd).forall { fde => !purescala.ExprOps.exists( _.isInstanceOf[Choose])(fde.fullBody) } - }) match { - case Some(fd) => - //println("Found fd: " + fd.id.name) - val ste = new StringTracingEvaluator(ctx, program) - try { - val result = ste.eval(FunctionInvocation(fd.typed, Seq(v))) + if(isCandidate) { + // InputType is concrete, the types of params may be abstract. + TypeOps.canBeSubtypeOf(inputType, fd.tparams.map(_.tp), fd.params.head.getType) match { + case Some(genericTypeMap) => + 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 { + 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 + } + } + 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", fd.params.head.getType) // verify the type + Lambda(Seq(ValDef(x)), functionInvocation(fd, Variable(x)::lambdas)) + } + case _ => Nil + } + case None => Nil + } + } else Nil + }).toStream + } + + /** Actually prints the expression with as alternative the given orElse */ + def print(v: Expr, orElse: =>String, excluded: FunDef => Boolean = Set())(implicit ctx: LeonContext, program: Program): String = { + val s = prettyPrintersForType(v.getType) + if(s.isEmpty) { + orElse + } else { + val l: Lambda = s.head + val ste = new DefaultEvaluator(ctx, program) + try { + val toEvaluate = application(l, Seq(v)) + val result = ste.eval(toEvaluate) result.result match { - case Some((StringLiteral(res), _)) if res != "" => + case Some(StringLiteral(res)) if res != "" => res - case _ => - orElse - } - } catch { - case e: evaluators.ContextualEvaluator#EvalError => + case res => orElse } - case None => - orElse + } catch { + case e: evaluators.ContextualEvaluator#EvalError => + orElse + } } } } \ No newline at end of file -- GitLab