From 282f172c2f669311648d3621fa17c5d3b17ca9ac Mon Sep 17 00:00:00 2001 From: Nicolas Voirol <voirol.nicolas@gmail.com> Date: Wed, 13 Jul 2016 10:24:17 +0200 Subject: [PATCH] Added datagen, continued working on trees --- .../scala/inox/datagen/DataGenerator.scala | 26 + .../scala/inox/datagen/GrammarDataGen.scala | 94 ++ .../scala/inox/datagen/NaiveDataGen.scala | 104 +++ .../scala/inox/datagen/SolverDataGen.scala | 83 ++ .../scala/inox/datagen/VanuatooDataGen.scala | 409 +++++++++ .../inox/evaluators/AbstractEvaluator.scala | 357 -------- .../evaluators/AbstractOnlyEvaluator.scala | 275 ------ .../inox/evaluators/AngelicEvaluator.scala | 46 - .../inox/evaluators/CodeGenEvaluator.scala | 78 -- .../scala/inox/evaluators/DualEvaluator.scala | 138 --- .../inox/evaluators/EvaluationPhase.scala | 55 -- .../inox/evaluators/ScalacEvaluator.scala | 459 ---------- .../inox/evaluators/StreamEvaluator.scala | 718 --------------- .../inox/evaluators/TracingEvaluator.scala | 118 --- .../inox/evaluators/TrackingEvaluator.scala | 124 --- .../scala/inox/solvers/EvaluatingSolver.scala | 20 - src/main/scala/inox/solvers/RawArray.scala | 81 -- .../inox/solvers/string/StringSolver.scala | 18 +- .../unrolling}/Quantification.scala | 0 src/main/scala/inox/trees/CallGraph.scala | 4 +- src/main/scala/inox/trees/Constructors.scala | 17 +- .../inox/trees/DefinitionTransformer.scala | 218 ----- src/main/scala/inox/trees/Definitions.scala | 20 +- .../scala/inox/trees/DependencyFinder.scala | 103 --- src/main/scala/inox/trees/ExprOps.scala | 14 +- src/main/scala/inox/trees/Extractors.scala | 14 +- src/main/scala/inox/trees/GenTreeOps.scala | 24 +- src/main/scala/inox/trees/Path.scala | 231 ----- src/main/scala/inox/trees/Paths.scala | 232 +++++ src/main/scala/inox/trees/PrettyPrinter.scala | 756 ---------------- .../scala/inox/trees/PrinterHelpers.scala | 98 -- src/main/scala/inox/trees/Printers.scala | 835 ++++++++++++++++++ src/main/scala/inox/trees/Transformer.scala | 10 - .../scala/inox/trees/TransformerWithPC.scala | 101 --- .../scala/inox/trees/TreeTransformer.scala | 242 ----- src/main/scala/inox/trees/Trees.scala | 22 +- src/main/scala/inox/trees/TypeOps.scala | 15 +- src/main/scala/inox/trees/Types.scala | 16 +- src/main/scala/inox/trees/package.scala | 4 +- 39 files changed, 1857 insertions(+), 4322 deletions(-) create mode 100644 src/main/scala/inox/datagen/DataGenerator.scala create mode 100644 src/main/scala/inox/datagen/GrammarDataGen.scala create mode 100644 src/main/scala/inox/datagen/NaiveDataGen.scala create mode 100644 src/main/scala/inox/datagen/SolverDataGen.scala create mode 100644 src/main/scala/inox/datagen/VanuatooDataGen.scala delete mode 100644 src/main/scala/inox/evaluators/AbstractEvaluator.scala delete mode 100644 src/main/scala/inox/evaluators/AbstractOnlyEvaluator.scala delete mode 100644 src/main/scala/inox/evaluators/AngelicEvaluator.scala delete mode 100644 src/main/scala/inox/evaluators/CodeGenEvaluator.scala delete mode 100644 src/main/scala/inox/evaluators/DualEvaluator.scala delete mode 100644 src/main/scala/inox/evaluators/EvaluationPhase.scala delete mode 100644 src/main/scala/inox/evaluators/ScalacEvaluator.scala delete mode 100644 src/main/scala/inox/evaluators/StreamEvaluator.scala delete mode 100644 src/main/scala/inox/evaluators/TracingEvaluator.scala delete mode 100644 src/main/scala/inox/evaluators/TrackingEvaluator.scala delete mode 100644 src/main/scala/inox/solvers/EvaluatingSolver.scala delete mode 100644 src/main/scala/inox/solvers/RawArray.scala rename src/main/scala/inox/{trees => solvers/unrolling}/Quantification.scala (100%) delete mode 100644 src/main/scala/inox/trees/DefinitionTransformer.scala delete mode 100644 src/main/scala/inox/trees/DependencyFinder.scala delete mode 100644 src/main/scala/inox/trees/Path.scala create mode 100644 src/main/scala/inox/trees/Paths.scala delete mode 100644 src/main/scala/inox/trees/PrettyPrinter.scala delete mode 100644 src/main/scala/inox/trees/PrinterHelpers.scala create mode 100644 src/main/scala/inox/trees/Printers.scala delete mode 100644 src/main/scala/inox/trees/Transformer.scala delete mode 100644 src/main/scala/inox/trees/TransformerWithPC.scala delete mode 100644 src/main/scala/inox/trees/TreeTransformer.scala diff --git a/src/main/scala/inox/datagen/DataGenerator.scala b/src/main/scala/inox/datagen/DataGenerator.scala new file mode 100644 index 000000000..ea6aac313 --- /dev/null +++ b/src/main/scala/inox/datagen/DataGenerator.scala @@ -0,0 +1,26 @@ +/* Copyright 2009-2016 EPFL, Lausanne */ + +package leon +package datagen + +import purescala.Expressions._ +import purescala.Common._ +import utils._ + +import java.util.concurrent.atomic.AtomicBoolean + +trait DataGenerator extends Interruptible { + implicit val debugSection = DebugSectionDataGen + + def generateFor(ins: Seq[Identifier], satisfying: Expr, maxValid: Int, maxEnumerated: Int): Iterator[Seq[Expr]] + + protected val interrupted: AtomicBoolean = new AtomicBoolean(false) + + def interrupt(): Unit = { + interrupted.set(true) + } + + def recoverInterrupt(): Unit = { + interrupted.set(false) + } +} diff --git a/src/main/scala/inox/datagen/GrammarDataGen.scala b/src/main/scala/inox/datagen/GrammarDataGen.scala new file mode 100644 index 000000000..ffe7b8b47 --- /dev/null +++ b/src/main/scala/inox/datagen/GrammarDataGen.scala @@ -0,0 +1,94 @@ +/* Copyright 2009-2016 EPFL, Lausanne */ + +package leon +package datagen + +import purescala.Expressions._ +import purescala.Types._ +import purescala.Common._ +import purescala.Constructors._ +import purescala.Extractors._ +import purescala.ExprOps._ +import evaluators._ +import bonsai.enumerators._ + +import grammars._ +import utils.UniqueCounter +import utils.SeqUtils.cartesianProduct + +/** Utility functions to generate values of a given type. + * In fact, it could be used to generate *terms* of a given type, + * e.g. by passing trees representing variables for the "bounds". */ +class GrammarDataGen(evaluator: Evaluator, grammar: ExpressionGrammar = ValueGrammar) extends DataGenerator { + implicit val ctx = evaluator.context + + // Assume e contains generic values with index 0. + // Return a series of expressions with all normalized combinations of generic values. + private def expandGenerics(e: Expr): Seq[Expr] = { + val c = new UniqueCounter[TypeParameter] + val withUniqueCounters: Expr = postMap { + case GenericValue(t, _) => + Some(GenericValue(t, c.next(t))) + case _ => None + }(e) + + val indices = c.current + + val (tps, substInt) = (for { + tp <- indices.keySet.toSeq + } yield tp -> (for { + from <- 0 to indices(tp) + to <- 0 to from + } yield (from, to))).unzip + + val combos = cartesianProduct(substInt) + + val substitutions = combos map { subst => + tps.zip(subst).map { case (tp, (from, to)) => + (GenericValue(tp, from): Expr) -> (GenericValue(tp, to): Expr) + }.toMap + } + + substitutions map (replace(_, withUniqueCounters)) + + } + + def generate(tpe: TypeTree): Iterator[Expr] = { + val enum = new MemoizedEnumerator[Label, Expr, ProductionRule[Label, Expr]](grammar.getProductions) + enum.iterator(Label(tpe)).flatMap(expandGenerics).takeWhile(_ => !interrupted.get) + } + + def generateFor(ins: Seq[Identifier], satisfying: Expr, maxValid: Int, maxEnumerated: Int): Iterator[Seq[Expr]] = { + + def filterCond(vs: Seq[Expr]): Boolean = satisfying match { + case BooleanLiteral(true) => + true + case e => + // in -> e should be enough. We shouldn't find any subexpressions of in. + evaluator.eval(e, (ins zip vs).toMap) match { + case EvaluationResults.Successful(BooleanLiteral(true)) => true + case _ => false + } + } + + if (ins.isEmpty) { + Iterator(Seq[Expr]()).filter(filterCond) + } else { + val values = generate(tupleTypeWrap(ins.map{ _.getType })) + + val detupled = values.map { + v => unwrapTuple(v, ins.size) + } + + detupled.take(maxEnumerated) + .filter(filterCond) + .take(maxValid) + .takeWhile(_ => !interrupted.get) + } + } + + def generateMapping(ins: Seq[Identifier], satisfying: Expr, maxValid: Int, maxEnumerated: Int) = { + generateFor(ins, satisfying, maxValid, maxEnumerated) map (ins zip _) + } + +} diff --git a/src/main/scala/inox/datagen/NaiveDataGen.scala b/src/main/scala/inox/datagen/NaiveDataGen.scala new file mode 100644 index 000000000..e40359e68 --- /dev/null +++ b/src/main/scala/inox/datagen/NaiveDataGen.scala @@ -0,0 +1,104 @@ +/* Copyright 2009-2016 EPFL, Lausanne */ + +package leon +package datagen + +import purescala.Common._ +import purescala.Expressions._ +import purescala.Types._ +import purescala.Definitions._ +import purescala.Quantification._ +import utils.StreamUtils._ + +import evaluators._ + +import scala.collection.mutable.{Map=>MutableMap} + +/** Utility functions to generate values of a given type. + * In fact, it could be used to generate *terms* of a given type, + * e.g. by passing trees representing variables for the "bounds". */ +@deprecated("Stream-based datagen is deprecated, use GrammarDataGen with ValueGrammar instead", "3.0") +class NaiveDataGen(ctx: LeonContext, p: Program, evaluator: Evaluator, _bounds : Option[Map[TypeTree,Seq[Expr]]] = None) extends DataGenerator { + + val bounds = _bounds.getOrElse(Map()) + + private def tpStream(tp: TypeParameter, i: Int = 1): Stream[Expr] = Stream.cons(GenericValue(tp, i), tpStream(tp, i+1)) + + private val streamCache : MutableMap[TypeTree,Stream[Expr]] = MutableMap.empty + def generate(tpe : TypeTree) : Stream[Expr] = { + try { + streamCache.getOrElse(tpe, { + val s = generate0(tpe) + streamCache(tpe) = s + s + }) + } catch { + case so: StackOverflowError => + Stream.empty + } + } + + private def generate0(tpe: TypeTree): Stream[Expr] = bounds.get(tpe).map(_.toStream).getOrElse { + tpe match { + case BooleanType => + BooleanLiteral(true) #:: BooleanLiteral(false) #:: Stream.empty + + case Int32Type => + IntLiteral(0) #:: IntLiteral(1) #:: IntLiteral(2) #:: IntLiteral(-1) #:: Stream.empty + + case tp: TypeParameter => + tpStream(tp) + + case TupleType(bses) => + cartesianProduct(bses.map(generate)).map(Tuple) + + case act : AbstractClassType => + // We prioritize base cases among the children. + // Otherwise we run the risk of infinite recursion when + // generating lists. + val ccChildren = act.knownCCDescendants + + val (leafs,conss) = ccChildren.partition(_.fields.isEmpty) + + // FIXME: Will not work for mutually recursive types + val sortedConss = conss sortBy { _.fields.count{ _.getType.isInstanceOf[ClassType]}} + + // The stream for leafs... + val leafsStream = leafs.toStream.flatMap(generate) + + // ...to which we append the streams for constructors. + leafsStream.append(interleave(sortedConss.map(generate))) + + case cct : CaseClassType => + if(cct.fields.isEmpty) { + Stream.cons(CaseClass(cct, Nil), Stream.empty) + } else { + val fieldTypes = cct.fieldsTypes + cartesianProduct(fieldTypes.map(generate)).map(CaseClass(cct, _)) + } + + case _ => Stream.empty + } + } + + def generateFor(ins: Seq[Identifier], satisfying: Expr, maxValid : Int, maxEnumerated : Int) : Iterator[Seq[Expr]] = { + val filtering = if (satisfying == BooleanLiteral(true)) { + { (e: Seq[Expr]) => true } + } else { + evaluator.compile(satisfying, ins).map { evalFun => + val sat = EvaluationResults.Successful(BooleanLiteral(true)) + + { (e: Seq[Expr]) => evalFun(new solvers.Model((ins zip e).toMap)) == sat } + } getOrElse { + { (e: Seq[Expr]) => false } + } + } + + cartesianProduct(ins.map(id => generate(id.getType))) + .take(maxEnumerated) + .takeWhile(s => !interrupted.get) + .filter{filtering} + .take(maxValid) + .iterator + } +} diff --git a/src/main/scala/inox/datagen/SolverDataGen.scala b/src/main/scala/inox/datagen/SolverDataGen.scala new file mode 100644 index 000000000..37dcfba06 --- /dev/null +++ b/src/main/scala/inox/datagen/SolverDataGen.scala @@ -0,0 +1,83 @@ +/* Copyright 2009-2016 EPFL, Lausanne */ + +package leon +package datagen + +import purescala.Expressions._ +import purescala.Types._ +import purescala.Definitions._ +import purescala.Common._ +import purescala.Constructors._ +import solvers._ +import utils._ + +class SolverDataGen(ctx: LeonContext, pgm: Program, sf: SolverFactory[Solver]) extends DataGenerator { + implicit val ctx0 = ctx + + def generate(tpe: TypeTree): FreeableIterator[Expr] = { + generateFor(Seq(FreshIdentifier("tmp", tpe)), BooleanLiteral(true), 20, 20).map(_.head).takeWhile(_ => !interrupted.get) + } + + def generateFor(ins: Seq[Identifier], satisfying: Expr, maxValid: Int, maxEnumerated: Int): FreeableIterator[Seq[Expr]] = { + if (ins.isEmpty) { + FreeableIterator.empty + } else { + + var fds = Map[ClassDef, FunDef]() + + def sizeFor(of: Expr): Expr = of.getType match { + case AbstractClassType(acd, tps) => + val fd = fds.getOrElse(acd, { + val actDef = AbstractClassType(acd, acd.tparams.map(_.tp)) + + val e = FreshIdentifier("e", actDef) + + val fd: FunDef = new FunDef(FreshIdentifier("sizeOf", Untyped), acd.tparams, Seq(ValDef(e)), IntegerType) + + fds += acd -> fd + + + fd.body = Some(MatchExpr(e.toVariable, + actDef.knownCCDescendants.map { cct => + val fields = cct.fieldsTypes.map ( t => FreshIdentifier("field", t)) + + val rhs = fields.foldLeft(InfiniteIntegerLiteral(1): Expr) { (i, f) => + plus(i, sizeFor(f.toVariable)) + } + + MatchCase(CaseClassPattern(None, cct, fields.map(f => WildcardPattern(Some(f)))), None, rhs) + } + )) + + fd + }) + + FunctionInvocation(fd.typed(tps), Seq(of)) + + case tt @ TupleType(tps) => + val exprs = for ((t,i) <- tps.zipWithIndex) yield { + sizeFor(tupleSelect(of, i+1, tps.size)) + } + + exprs.foldLeft(InfiniteIntegerLiteral(1): Expr)(plus) + + case _ => + InfiniteIntegerLiteral(1) + } + + val sizeOf = sizeFor(tupleWrap(ins.map(_.toVariable))) + + // We need to synthesize a size function for ins' types. + val pgm1 = Program(pgm.units :+ UnitDef(FreshIdentifier("new"), List( + ModuleDef(FreshIdentifier("new"), fds.values.toSeq, false) + ))) + + val modelEnum = new ModelEnumerator(ctx, pgm1, sf) + + val enum = modelEnum.enumVarying(ins, satisfying, sizeOf, 5) + + enum.take(maxValid).map(model => ins.map(model)).takeWhile(_ => !interrupted.get) + } + } + +} diff --git a/src/main/scala/inox/datagen/VanuatooDataGen.scala b/src/main/scala/inox/datagen/VanuatooDataGen.scala new file mode 100644 index 000000000..ccba55af9 --- /dev/null +++ b/src/main/scala/inox/datagen/VanuatooDataGen.scala @@ -0,0 +1,409 @@ +/* Copyright 2009-2016 EPFL, Lausanne */ + +package leon +package datagen + +import purescala.Common._ +import purescala.Definitions._ +import purescala.ExprOps._ +import purescala.Expressions._ +import purescala.Types._ +import purescala.Extractors._ +import purescala.Constructors._ + +import codegen.CompilationUnit +import codegen.CodeGenParams +import codegen.runtime.StdMonitor +import vanuatoo.{Pattern => VPattern, _} + +import evaluators._ + +class VanuatooDataGen(ctx: LeonContext, p: Program, bank: EvaluationBank = new EvaluationBank) extends DataGenerator { + val unit = new CompilationUnit(ctx, p, bank, CodeGenParams.default.copy(doInstrument = true)) + + val ints = (for (i <- Set(0, 1, 2, 3)) yield { + i -> Constructor[Expr, TypeTree](List(), Int32Type, s => IntLiteral(i), ""+i) + }).toMap + + val bigInts = (for (i <- Set(0, 1, 2, 3)) yield { + i -> Constructor[Expr, TypeTree](List(), IntegerType, s => InfiniteIntegerLiteral(i), ""+i) + }).toMap + + val booleans = (for (b <- Set(true, false)) yield { + b -> Constructor[Expr, TypeTree](List(), BooleanType, s => BooleanLiteral(b), ""+b) + }).toMap + + val chars = (for (c <- Set('a', 'b', 'c', 'd')) yield { + c -> Constructor[Expr, TypeTree](List(), CharType, s => CharLiteral(c), ""+c) + }).toMap + + val rationals = (for (n <- Set(0, 1, 2, 3); d <- Set(1,2,3,4)) yield { + (n, d) -> Constructor[Expr, TypeTree](List(), RealType, s => FractionalLiteral(n, d), "" + n + "/" + d) + }).toMap + + val strings = (for (b <- Set("", "a", "foo", "bar")) yield { + b -> Constructor[Expr, TypeTree](List(), StringType, s => StringLiteral(b), b) + }).toMap + + + def intConstructor(i: Int) = ints(i) + + def bigIntConstructor(i: Int) = bigInts(i) + + def boolConstructor(b: Boolean) = booleans(b) + + def charConstructor(c: Char) = chars(c) + + def rationalConstructor(n: Int, d: Int) = rationals(n -> d) + + def stringConstructor(s: String) = strings(s) + + lazy val stubValues = ints.values ++ bigInts.values ++ booleans.values ++ chars.values ++ rationals.values ++ strings.values + + def cPattern(c: Constructor[Expr, TypeTree], args: Seq[VPattern[Expr, TypeTree]]) = { + ConstructorPattern[Expr, TypeTree](c, args) + } + + private var constructors = Map[TypeTree, List[Constructor[Expr, TypeTree]]]() + + private def getConstructorFor(t: CaseClassType, act: AbstractClassType): Constructor[Expr, TypeTree] = { + // We "up-cast" the returnType of the specific caseclass generator to match its superclass + getConstructors(t).head.copy(retType = act) + } + + private def getConstructors(t: TypeTree): List[Constructor[Expr, TypeTree]] = t match { + case UnitType => + constructors.getOrElse(t, { + val cs = List(Constructor[Expr, TypeTree](List(), t, s => UnitLiteral(), "()")) + constructors += t -> cs + cs + }) + + case at @ ArrayType(sub) => + constructors.getOrElse(at, { + val cs = for (size <- List(0, 1, 2, 5)) yield { + Constructor[Expr, TypeTree]( + (1 to size).map(i => sub).toList, + at, + s => finiteArray(s, None, sub), + at.asString(ctx)+"@"+size + ) + } + constructors += at -> cs + cs + }) + + case st @ SetType(sub) => + constructors.getOrElse(st, { + val cs = for (size <- List(0, 1, 2, 5)) yield { + Constructor[Expr, TypeTree]( + (1 to size).map(i => sub).toList, + st, + s => FiniteSet(s.toSet, sub), + st.asString(ctx)+"@"+size + ) + } + constructors += st -> cs + cs + }) + + case bt @ BagType(sub) => + constructors.getOrElse(bt, { + val cs = for (size <- List(0, 1, 2, 5)) yield { + val subs = (1 to size).flatMap(i => List(sub, IntegerType)).toList + Constructor[Expr, TypeTree](subs, bt, s => FiniteBag(s.grouped(2).map { + case Seq(k, i @ InfiniteIntegerLiteral(v)) => + k -> (if (v > 0) i else InfiniteIntegerLiteral(-v + 1)) + }.toMap, sub), bt.asString(ctx)+"@"+size) + } + constructors += bt -> cs + cs + }) + + case tt @ TupleType(parts) => + constructors.getOrElse(tt, { + val cs = List(Constructor[Expr, TypeTree](parts, tt, s => tupleWrap(s), tt.asString(ctx))) + constructors += tt -> cs + cs + }) + + case mt @ MapType(from, to) => + constructors.getOrElse(mt, { + val cs = for (size <- List(0, 1, 2, 5)) yield { + val subs = (1 to size).flatMap(i => List(from, to)).toList + Constructor[Expr, TypeTree](subs, mt, s => FiniteMap(s.grouped(2).map(t => (t(0), t(1))).toMap, from, to), mt.asString(ctx)+"@"+size) + } + constructors += mt -> cs + cs + }) + + case ft @ FunctionType(from, to) => + constructors.getOrElse(ft, { + val cs = for (size <- List(1, 2, 3, 5)) yield { + val subs = (1 to size).flatMap(_ => from :+ to).toList + Constructor[Expr, TypeTree](subs, ft, { s => + val grouped = s.grouped(from.size + 1).toSeq + val mapping = grouped.init.map { case args :+ res => (args -> res) } + FiniteLambda(mapping, grouped.last.last, ft) + }, ft.asString(ctx) + "@" + size) + } + constructors += ft -> cs + cs + }) + + case tp: TypeParameter => + constructors.getOrElse(tp, { + val cs = for (i <- List(1, 2)) yield { + Constructor[Expr, TypeTree](List(), tp, s => GenericValue(tp, i), tp.id+"#"+i) + } + constructors += tp -> cs + cs + }) + + case act: AbstractClassType => + constructors.getOrElse(act, { + val cs = act.knownCCDescendants.map { + cct => getConstructorFor(cct, act) + }.toList + + constructors += act -> cs + + cs + }) + + case cct: CaseClassType => + constructors.getOrElse(cct, { + val c = List(Constructor[Expr, TypeTree](cct.fieldsTypes, cct, s => CaseClass(cct, s), cct.id.name)) + constructors += cct -> c + c + }) + + case _ => + ctx.reporter.error("Unknown type to generate constructor for: "+t) + Nil + } + + // Returns the pattern and whether it is fully precise + private def valueToPattern(v: AnyRef, expType: TypeTree): (VPattern[Expr, TypeTree], Boolean) = (v, expType) match { + case (i: Integer, Int32Type) => + (cPattern(intConstructor(i), List()), true) + + case (i: Integer, IntegerType) => + (cPattern(bigIntConstructor(i), List()), true) + + case (b: java.lang.Boolean, BooleanType) => + (cPattern(boolConstructor(b), List()), true) + + case (c: java.lang.Character, CharType) => + (cPattern(charConstructor(c), List()), true) + + case (b: java.lang.String, StringType) => + (cPattern(stringConstructor(b), List()), true) + + case (cc: codegen.runtime.CaseClass, ct: ClassType) => + val r = cc.__getRead() + + unit.jvmClassToLeonClass(cc.getClass.getName) match { + case Some(ccd: CaseClassDef) => + val cct = CaseClassType(ccd, ct.tps) + val c = ct match { + case act : AbstractClassType => + getConstructorFor(cct, act) + case cct : CaseClassType => + getConstructors(cct).head + } + + val fields = cc.productElements() + + val elems = for (i <- 0 until fields.length) yield { + if (((r >> i) & 1) == 1) { + // has been read + valueToPattern(fields(i), cct.fieldsTypes(i)) + } else { + (AnyPattern[Expr, TypeTree](), false) + } + } + + (ConstructorPattern(c, elems.map(_._1)), elems.forall(_._2)) + + case _ => + ctx.reporter.error("Could not retrieve type for :"+cc.getClass.getName) + (AnyPattern[Expr, TypeTree](), false) + } + + case (t: codegen.runtime.Tuple, tpe) => + val r = t.__getRead() + + val parts = unwrapTupleType(tpe, t.getArity) + + val c = getConstructors(tpe).head + + val elems = for (i <- 0 until t.getArity) yield { + if (((r >> i) & 1) == 1) { + // has been read + valueToPattern(t.get(i), parts(i)) + } else { + (AnyPattern[Expr, TypeTree](), false) + } + } + + (ConstructorPattern(c, elems.map(_._1)), elems.forall(_._2)) + + case (gv: GenericValue, t: TypeParameter) => + (cPattern(getConstructors(t)(gv.id-1), List()), true) + + case (v, t) => + ctx.reporter.debug("Unsupported value, can't paternify : "+v+" ("+v.getClass+") : "+t) + (AnyPattern[Expr, TypeTree](), false) + } + + type InstrumentedResult = (EvaluationResults.Result[Expr], Option[vanuatoo.Pattern[Expr, TypeTree]]) + + def compile(expression: Expr, argorder: Seq[Identifier]) : Option[Expr=>InstrumentedResult] = { + import leon.codegen.runtime.LeonCodeGenRuntimeException + import leon.codegen.runtime.LeonCodeGenEvaluationException + + try { + val ttype = tupleTypeWrap(argorder.map(_.getType)) + val tid = FreshIdentifier("tup", ttype) + + val map = argorder.zipWithIndex.map{ case (id, i) => id -> tupleSelect(Variable(tid), i + 1, argorder.size) }.toMap + + val newExpr = replaceFromIDs(map, expression) + + val ce = unit.compileExpression(newExpr, Seq(tid))(ctx) + + Some((args : Expr) => { + try { + val monitor = new StdMonitor(unit, unit.params.maxFunctionInvocations, Map()) + + val jvmArgs = ce.argsToJVM(Seq(args), monitor) + + val result = ce.evalFromJVM(jvmArgs, monitor) + + // jvmArgs is getting updated by evaluating + val pattern = valueToPattern(jvmArgs.head, ttype) + + (EvaluationResults.Successful(result), if (!pattern._2) Some(pattern._1) else None) + } catch { + case e : StackOverflowError => + (EvaluationResults.RuntimeError(e.getMessage), None) + + case e : ClassCastException => + (EvaluationResults.RuntimeError(e.getMessage), None) + + case e : ArithmeticException => + (EvaluationResults.RuntimeError(e.getMessage), None) + + case e : ArrayIndexOutOfBoundsException => + (EvaluationResults.RuntimeError(e.getMessage), None) + + case e : LeonCodeGenRuntimeException => + (EvaluationResults.RuntimeError(e.getMessage), None) + + case e : LeonCodeGenEvaluationException => + (EvaluationResults.EvaluatorError(e.getMessage), None) + } + }) + } catch { + case t: Throwable => + ctx.reporter.warning("Error while compiling expression: "+t.getMessage); t.printStackTrace() + None + } + } + + def generateFor(ins: Seq[Identifier], satisfying: Expr, maxValid: Int, maxEnumerated: Int): Iterator[Seq[Expr]] = { + // Split conjunctions + val TopLevelAnds(ands) = satisfying + + val runners = ands.flatMap(a => compile(a, ins) match { + case Some(runner) => Some(runner) + case None => + ctx.reporter.error("Could not compile predicate " + a) + None + }) + + val gen = new StubGenerator[Expr, TypeTree](stubValues.toSeq, + Some(getConstructors _), + treatEmptyStubsAsChildless = true) + + /** + * Gather at most <n> isomoprhic models before skipping them + * - Too little means skipping many excluding patterns + * - Too large means repetitive (and not useful models) before reaching maxEnumerated + */ + + val maxIsomorphicModels = maxValid+1 + + val it = gen.enumerate(tupleTypeWrap(ins.map(_.getType))) + + new Iterator[Seq[Expr]] { + var total = 0 + var found = 0 + + var theNext: Option[Seq[Expr]] = None + + def hasNext = { + if (total == 0) { + theNext = computeNext() + } + + theNext.isDefined + } + + def next() = { + val res = theNext.get + theNext = computeNext() + res + } + + + def computeNext(): Option[Seq[Expr]] = { + //return None + while (total < maxEnumerated && found < maxValid && it.hasNext && !interrupted.get) { + val model = it.next() + it.hasNext // FIXME: required for some reason by StubGenerator or will return false during loop check + + if (model eq null) { + total = maxEnumerated + } else { + total += 1 + + var failed = false + + for (r <- runners) r(model) match { + case (EvaluationResults.Successful(BooleanLiteral(true)), _) => + + case (_, Some(pattern)) => + failed = true + it.exclude(pattern) + + case (_, None) => + failed = true; + } + + if (!failed) { + //println("Got model:") + //for ((i, v) <- (ins zip model.exprs)) { + // println(" - "+i+" -> "+v) + //} + + found += 1 + + if (found % maxIsomorphicModels == 0) { + it.skipIsomorphic() + } + + return Some(unwrapTuple(model, ins.size)) + } + + //if (total % 1000 == 0) { + // println("... "+total+" ...") + //} + } + } + None + } + } + } +} diff --git a/src/main/scala/inox/evaluators/AbstractEvaluator.scala b/src/main/scala/inox/evaluators/AbstractEvaluator.scala deleted file mode 100644 index 142628aff..000000000 --- a/src/main/scala/inox/evaluators/AbstractEvaluator.scala +++ /dev/null @@ -1,357 +0,0 @@ -/* Copyright 2009-2015 EPFL, Lausanne */ - -package leon -package evaluators - -import purescala.Extractors.Operator -import purescala.Constructors._ -import purescala.Expressions._ -import purescala.Types._ -import purescala.Common.Identifier -import purescala.Definitions.{TypedFunDef, Program} -import purescala.DefOps -import purescala.TypeOps -import purescala.ExprOps -import purescala.Expressions.Expr -import leon.utils.DebugSectionSynthesis - -case class AbstractRecContext(mappings: Map[Identifier, Expr], mappingsAbstract: Map[Identifier, Expr]) extends RecContext[AbstractRecContext] { - def newVars(news: Map[Identifier, Expr], newsAbstract: Map[Identifier, Expr]): AbstractRecContext = copy(news, newsAbstract) - def newVars(news: Map[Identifier, Expr]): AbstractRecContext = copy(news, news) - - def withNewVar(id: Identifier, v: (Expr, Expr)): AbstractRecContext = { - newVars(mappings + (id -> v._1), mappingsAbstract + (id -> v._2)) - } - - def withNewVars2(news: Map[Identifier, (Expr, Expr)]): AbstractRecContext = { - newVars(mappings ++ news.mapValues(_._1), mappingsAbstract ++ news.mapValues(_._2)) - } - - override def withNewVar(id: Identifier, v: Expr): AbstractRecContext = { - newVars(mappings + (id -> v), mappingsAbstract + (id -> v)) - } - - override def withNewVars(news: Map[Identifier, Expr]): AbstractRecContext = { - newVars(mappings ++ news, mappingsAbstract ++ news) - } -} - - -trait HasAbstractRecContext extends ContextualEvaluator { - type RC = AbstractRecContext - def initRC(mappings: Map[Identifier, Expr]) = AbstractRecContext(mappings, mappings) -} - -/** The evaluation returns a pair (e, t), - * where e is the expression evaluated as much as possible, and t is the way the expression has been evaluated. - * Caution: If and Match statement require the condition to be non-abstract. */ -class AbstractEvaluator(ctx: LeonContext, prog: Program) extends ContextualEvaluator(ctx, prog, 50000) with HasDefaultGlobalContext with HasAbstractRecContext { - lazy val scalaEv = new ScalacEvaluator(underlying, ctx, prog) - - /** Evaluates resuts which can be evaluated directly - * For example, concatenation of two string literals */ - val underlying = new DefaultEvaluator(ctx, prog) - underlying.setEvaluationFailOnChoose(true) - override type Value = (Expr, Expr) - - override val description: String = "Evaluates string programs but keeps the formula which generated the value" - override val name: String = "Abstract evaluator" - - /** True if CaseClassSelector(...CaseClass(...)) have to be simplified. */ - var evaluateCaseClassSelector = true - /** True if Application(Lambda(), ...) have to be simplified. */ - var evaluateApplications = true - - // Used to make the final mkString for maps, sets, and bags. First column is the key to sort the expression on, second are the values and third are the trees. - protected def mkString(elements: List[(String, Expr, Expr)], infix: (Expr, Expr)): (Expr, Expr) = { - val (infix_value, infix_tree) = infix - val (sorting_key, elements_value, elements_tree) = elements.sortBy(f => f._1).unzip3 - - val fst = infix_value match { - case StringLiteral(v) if elements_value.forall(_.isInstanceOf[StringLiteral]) => - StringLiteral(elements_value.map(_.asInstanceOf[StringLiteral].value).mkString(v)) - case infix_value => - elements_value match { - case Nil => StringLiteral("") - case a::q => q.foldLeft(a: Expr){ case (a, s) => StringConcat(StringConcat(a, infix_value), s) } - } - } - - val snd = elements_tree match { - case Nil => StringLiteral("") - case a::q => q.foldLeft(a){ case (a, s) => StringConcat(StringConcat(a, infix_tree), s) } - } - (fst, snd) - } - - protected def e(expr: Expr)(implicit rctx: RC, gctx: GC): (Expr, Expr) = { - implicit def aToC: AbstractEvaluator.this.underlying.RC = DefaultRecContext(rctx.mappings.filter{ case (k, v) => ExprOps.isValue(v) }) - expr match { - case Variable(id) => - (rctx.mappings.get(id), rctx.mappingsAbstract.get(id)) match { - case (Some(v), Some(va)) => - (v, va) - case _ => - (expr, expr) - } - - case e if ExprOps.isValue(e) => - (e, e) - - case IfExpr(cond, thenn, elze) => - val first = underlying.e(cond) - first match { - case BooleanLiteral(true) => - ctx.reporter.ifDebug(printer => printer(thenn))(DebugSectionSynthesis) - e(thenn) - case BooleanLiteral(false) => e(elze) - case _ => throw EvalError(typeErrorMsg(first, BooleanType)) - } - - case MatchExpr(scrut, cases) => - val (escrut, tscrut) = e(scrut) - cases.toStream.map(c => matchesCaseAbstract(escrut, tscrut, c)).find(_.nonEmpty) match { - case Some(Some((c, mappings))) => e(c.rhs)(rctx.withNewVars2(mappings), gctx) - case _ => throw RuntimeError("MatchError(Abstract evaluation): "+escrut.asString+" did not match any of the cases :\n" + cases.mkString("\n")) - } - - case FunctionInvocation(TypedFunDef(fd, Seq(ta, tb)), Seq(mp, inkv, betweenkv, fk, fv)) if fd == program.library.mapMkString.get => - val (inkv_str, inkv_tree) = e(inkv) - val infix = e(betweenkv) - val (mp_map, mp_tree) = e(mp) match { - case (FiniteMap(theMap, keyType, valueType), t) => (theMap, t) - case (e, f) => - println("First argument is not a finite map: " + e) - throw EvalError(typeErrorMsg(mp, MapType(ta, tb))) - } - - val res1 = mp_map.toList.map{ case (k, v) => - val (kvalue, ktree) = e(application(fk, Seq(k))) - val (vvalue, vtree) = e(application(fv, Seq(v))) - val abs_value = StringConcat(StringConcat(ktree, inkv_tree), vtree) - kvalue match { - case StringLiteral(k) => - if(ExprOps.isValue(vvalue) && ExprOps.isValue(inkv_str)) { - underlying.e(StringConcat(StringConcat(kvalue, inkv_str), vvalue)) match { - case sl@StringLiteral(s) => (s, sl, abs_value) - case e => throw RuntimeError("Not a string literal: " + e) - } - } else { - (k, StringConcat(StringConcat(kvalue, inkv_str), vvalue), abs_value) - } - case _ => - throw RuntimeError("cannot infer the order on which to print the map " + mp_map) - } - } - - mkString(res1, infix) - - case FunctionInvocation(TypedFunDef(fd, Seq(ta)), Seq(mp, inf, f)) if fd == program.library.setMkString.get => - val infix = e(inf) - val (mp_set, mp_tree) = e(mp) match { case (FiniteSet(elems, valueType), t) => (elems, t) case _ => throw EvalError(typeErrorMsg(mp, SetType(ta))) } - - val res = mp_set.toList.map{ case v => - e(application(f, Seq(v))) match { case (sl@StringLiteral(s), t) => (s, sl, t) - case _ => throw EvalError(typeErrorMsg(v, StringType)) } } - - mkString(res, infix) - - case FunctionInvocation(TypedFunDef(fd, Seq(ta)), Seq(mp, inf, f)) if fd == program.library.bagMkString.get => - val infix = e(inf) - val (mp_bag, mp_tree) = e(mp) match { case (FiniteBag(elems, valueType), t) => (elems, t) case _ => throw EvalError(typeErrorMsg(mp, SetType(ta))) } - - val res = mp_bag.toList.flatMap{ case (k, v) => - val fk = (e(application(f, Seq(k))) match { case (sl@StringLiteral(s), t) => (s, sl, t) case _ => throw EvalError(typeErrorMsg(k, StringType)) }) - val times = (e(v)) match { case (InfiniteIntegerLiteral(i), t) => i case _ => throw EvalError(typeErrorMsg(k, IntegerType)) } - List.fill(times.toString.toInt)(fk) - } - - mkString(res, infix) - - case FunctionInvocation(tfd, args) => - if (gctx.stepsLeft < 0) { - throw RuntimeError("Exceeded number of allocated methods calls ("+gctx.maxSteps+")") - } - gctx.stepsLeft -= 1 - val evArgs = args map e - val evArgsValues = evArgs.map(_._1) - val evArgsOrigin = evArgs.map(_._2) - - // build a mapping for the function... - val frame = rctx.withNewVars2((tfd.paramIds zip evArgs).toMap) - - val callResult = if ((evArgsValues forall ExprOps.isValue) && tfd.fd.annotations("extern") && ctx.classDir.isDefined) { - (scalaEv.call(tfd, evArgsValues), functionInvocation(tfd.fd, evArgsOrigin)) - } else { - if((!tfd.hasBody && !rctx.mappings.isDefinedAt(tfd.id)) || tfd.body.exists(b => ExprOps.exists(e => e.isInstanceOf[Choose])(b))) { - (functionInvocation(tfd.fd, evArgsValues), functionInvocation(tfd.fd, evArgsOrigin)) - } else { - val body = tfd.body.getOrElse(rctx.mappings(tfd.id)) - try { - e(body)(frame, gctx) - } catch { - case e: RuntimeError => - (functionInvocation(tfd.fd, evArgsValues), functionInvocation(tfd.fd, evArgsOrigin)) - } - } - } - callResult - - case Let(i, ex, b) => - val (first, second) = e(ex) - e(b)(rctx.withNewVar(i, (first, second)), gctx) - - case Application(caller, args) => - val (ecaller, tcaller) = e(caller) - val nargs = args map e - val (eargs, targs) = nargs.unzip - ecaller match { - case l @ Lambda(params, body) if evaluateApplications => - val mapping = (params map (_.id) zip nargs).toMap - e(body)(rctx.withNewVars2(mapping), gctx) - case _ => - val abs_value = Application(tcaller, targs) - if (ExprOps.isValue(ecaller) && (eargs forall ExprOps.isValue)) { - (underlying.e(Application(ecaller, eargs)), abs_value) - } else { - (Application(ecaller, eargs), abs_value) - } - } - - case l @ Lambda(_, _) => - import ExprOps._ - val mapping = variablesOf(l).map(id => id -> e(Variable(id))).toMap - ( - replaceFromIDs(mapping.mapValues(_._1), l).asInstanceOf[Lambda], - replaceFromIDs(mapping.mapValues(_._2), l).asInstanceOf[Lambda]) - - case Operator(es, builder) => - val (ees, ts) = es.map(e).unzip - if(ees forall ExprOps.isValue) { - val conc_value = underlying.e(builder(ees)) - val abs_value = builder(ts) - val final_abs_value = if( evaluateCaseClassSelector) { - abs_value match { - case CaseClassSelector(cct, CaseClass(ct, args), id) => - args(ct.classDef.selectorID2Index(id)) - case CaseClassSelector(cct, AsInstanceOf(CaseClass(ct, args), ccct), id) => - args(ct.classDef.selectorID2Index(id)) - case TupleSelect(Tuple(args), i) => - args(i-1) - case e => e - } - } else abs_value - - (conc_value, final_abs_value) - } else { - val abs_value = builder(ees) - val final_abs_value = if( evaluateCaseClassSelector) { - abs_value match { - case CaseClassSelector(cct, CaseClass(ct, args), id) => - args(ct.classDef.selectorID2Index(id)) - case CaseClassSelector(cct, AsInstanceOf(CaseClass(ct, args), ccct), id) => - args(ct.classDef.selectorID2Index(id)) - case TupleSelect(Tuple(args), i) => - args(i-1) - case e => e - } - } else abs_value - - (final_abs_value, builder(ts)) - } - } - } - - def matchesCaseAbstract(scrut: Expr, abstractScrut: Expr, caze: MatchCase)(implicit rctx: RC, gctx: GC): Option[(MatchCase, Map[Identifier, (Expr, Expr)])] = { - import purescala.TypeOps.isSubtypeOf - import purescala.Extractors._ - - def matchesPattern(pat: Pattern, expr: Expr, exprFromScrut: Expr): Option[Map[Identifier, (Expr, Expr)]] = (pat, expr) match { - case (InstanceOfPattern(ob, pct), e) => - if (isSubtypeOf(e.getType, pct)) { - Some(obind(ob, e, /*AsInstanceOf(*/exprFromScrut/*, pct)*/)) - } else { - None - } - case (WildcardPattern(ob), e) => - Some(obind(ob, e, exprFromScrut)) - - case (CaseClassPattern(ob, pct, subs), CaseClass(ct, args)) => - if (pct == ct) { - val res = (subs zip args zip ct.classDef.fieldsIds).map{ - case ((s, a), id) => - exprFromScrut match { - case CaseClass(ct, args) if evaluateCaseClassSelector => - matchesPattern(s, a, args(ct.classDef.selectorID2Index(id))) - case AsInstanceOf(CaseClass(ct, args), _) if evaluateCaseClassSelector => - matchesPattern(s, a, args(ct.classDef.selectorID2Index(id))) - case _ => - matchesPattern(s, a, CaseClassSelector(ct, exprFromScrut, id)) - } - } - if (res.forall(_.isDefined)) { - Some(obind(ob, expr, exprFromScrut) ++ res.flatten.flatten) - } else { - None - } - } else { - None - } - case (up @ UnapplyPattern(ob, _, subs), scrut) => - e(functionInvocation(up.unapplyFun.fd, Seq(scrut))) match { - case (CaseClass(CaseClassType(cd, _), Seq()), eBuilt) if cd == program.library.None.get => - None - case (CaseClass(CaseClassType(cd, _), Seq(arg)), eBuilt) if cd == program.library.Some.get => - val res = (subs zip unwrapTuple(arg, subs.size)).zipWithIndex map { - case ((s, a), i) => matchesPattern(s, a, tupleSelect(eBuilt, i + 1, subs.size)) - } - if (res.forall(_.isDefined)) { - Some(obind(ob, expr, eBuilt) ++ res.flatten.flatten) - } else { - None - } - case other => - throw EvalError(typeErrorMsg(other._1, up.unapplyFun.returnType)) - } - case (TuplePattern(ob, subs), Tuple(args)) => - if (subs.size == args.size) { - val res = (subs zip args).zipWithIndex.map{ - case ((s, a), i) => - exprFromScrut match { - case Tuple(args) if evaluateCaseClassSelector=> - matchesPattern(s, a, args(i)) - case _ => - matchesPattern(s, a, TupleSelect(exprFromScrut, i+1)) - } - } - if (res.forall(_.isDefined)) { - Some(obind(ob, expr, exprFromScrut) ++ res.flatten.flatten) - } else { - None - } - } else { - None - } - case (LiteralPattern(ob, l1) , l2 : Literal[_]) if l1 == l2 => - Some(obind(ob, l1, exprFromScrut)) - case _ => None - } - - def obind(ob: Option[Identifier], e: Expr, eBuilder: Expr): Map[Identifier, (Expr, Expr)] = { - Map[Identifier, (Expr, Expr)]() ++ ob.map(id => id -> ((e, eBuilder))) - } - - caze match { - case SimpleCase(p, rhs) => - matchesPattern(p, scrut, abstractScrut).map(r => - (caze, r) - ) - - case GuardedCase(p, g, rhs) => - for { - r <- matchesPattern(p, scrut, abstractScrut) - if e(g)(rctx.withNewVars2(r), gctx)._1 == BooleanLiteral(true) - } yield (caze, r) - } - } -} diff --git a/src/main/scala/inox/evaluators/AbstractOnlyEvaluator.scala b/src/main/scala/inox/evaluators/AbstractOnlyEvaluator.scala deleted file mode 100644 index 589213264..000000000 --- a/src/main/scala/inox/evaluators/AbstractOnlyEvaluator.scala +++ /dev/null @@ -1,275 +0,0 @@ -/* Copyright 2009-2015 EPFL, Lausanne */ - -package leon -package evaluators - -import purescala.Extractors.Operator -import purescala.Constructors._ -import purescala.Expressions._ -import purescala.Types._ -import purescala.Common.Identifier -import purescala.Definitions.{TypedFunDef, Program} -import purescala.DefOps -import purescala.TypeOps -import purescala.ExprOps -import purescala.Expressions.Expr -import leon.utils.DebugSectionSynthesis - -case class AbstractOnlyRecContext(concMapping: DefaultRecContext, mappingsAbstractOnly: Map[Identifier, Expr]) extends RecContext[AbstractOnlyRecContext] { - def newVars(news: Map[Identifier, Expr], newsAbstractOnly: Map[Identifier, Expr]): AbstractOnlyRecContext = copy(concMapping.newVars(news), newsAbstractOnly) - def newVars(news: Map[Identifier, Expr]): AbstractOnlyRecContext = copy(concMapping.newVars(news), news) - def mappings = concMapping.mappings - - def withNewVar(id: Identifier, v: Expr, vAbs: Expr): AbstractOnlyRecContext = { - newVars(mappings + (id -> v), mappingsAbstractOnly + (id -> vAbs)) - } - - /*def withNewVars2(news: Map[Identifier, (Expr, Expr)]): AbstractOnlyRecContext = { - newVars(mappings ++ news.mapValues(_._1), mappingsAbstractOnly ++ news.mapValues(_._2)) - }*/ - - def withNewVars3(news: Map[Identifier, Expr], newsAbstract: Map[Identifier, Expr]): AbstractOnlyRecContext = { - newVars(concMapping.mappings ++ news, mappingsAbstractOnly ++ newsAbstract) - } - - override def withNewVar(id: Identifier, v: Expr): AbstractOnlyRecContext = { - newVars(mappings + (id -> v), mappingsAbstractOnly + (id -> v)) - } - - override def withNewVars(news: Map[Identifier, Expr]): AbstractOnlyRecContext = { - newVars(mappings ++ news, mappingsAbstractOnly ++ news) - } -} - - -trait HasAbstractOnlyRecContext extends ContextualEvaluator { - type RC = AbstractOnlyRecContext - def initRC(mappings: Map[Identifier, Expr]) = AbstractOnlyRecContext(DefaultRecContext(mappings), mappings) -} - -/** The evaluation returns only the abstract value compared to the other implementation of [[leon.evaluators.AbstractEvaluator AbstractEvaluator]] - * It also supposes that everything can be computed else (i.e. not possible to add non-bound variables) - * @returns the way the expression has been evaluated. */ -class AbstractOnlyEvaluator(ctx: LeonContext, prog: Program, maxSteps: Int = 150000) extends ContextualEvaluator(ctx, prog, maxSteps) with HasDefaultGlobalContext with HasAbstractOnlyRecContext { - /** Evaluates resuts which can be evaluated directly - * For example, concatenation of two string literals */ - val underlying = new RecursiveEvaluator(ctx, prog, new EvaluationBank, maxSteps) - with HasDefaultGlobalContext - with HasDefaultRecContext - underlying.setEvaluationFailOnChoose(true) - override type Value = Expr - - override val description: String = "Evaluates string programs but keeps the formula which generated the value" - override val name: String = "AbstractOnly evaluator" - - /** True if CaseClassSelector(...CaseClass(...)) have to be simplified. */ - var evaluateCaseClassSelector = true - /** True if Application(Lambda(), ...) have to be simplified. */ - var evaluateApplications = true - - implicit def aToC(implicit rctx: RC): AbstractOnlyEvaluator.this.underlying.RC = rctx.concMapping - - protected def e(expr: Expr)(implicit rctx: RC, gctx: GC): Expr = { - expr match { - case Variable(id) => - rctx.mappingsAbstractOnly.get(id) match { - case Some(va) => - (va) - case _ => - expr - } - - case e : Literal[_] => - e - - case IfExpr(cond, thenn, elze) => - val first = underlying.e(cond) - first match { - case BooleanLiteral(true) => - ctx.reporter.ifDebug(printer => printer(thenn))(DebugSectionSynthesis) - e(thenn) - case BooleanLiteral(false) => e(elze) - case _ => throw EvalError(typeErrorMsg(first, BooleanType)) - } - - case MatchExpr(scrut, cases) => - val escrut = underlying.e(scrut) - val tscrut = e(scrut) - cases.toStream.map(c => matchesCaseAbstractOnly(escrut, tscrut, c)).find(_.nonEmpty) match { - case Some(Some((c, mappings))) => e(c.rhs)(rctx.withNewVars3(mappings.map(v => v._1 -> underlying.e(v._2)).toMap, mappings.toMap), gctx) - case _ => throw RuntimeError("MatchError(AbstractOnly evaluation): "+escrut.asString+" did not match any of the cases :\n" + cases.mkString("\n")) - } - - case FunctionInvocation(tfd, args) => - if (gctx.stepsLeft < 0) { - throw RuntimeError("Exceeded number of allocated methods calls ("+gctx.maxSteps+")") - } - gctx.stepsLeft -= 1 - - val evArgsValues = args map (underlying.e) - val evArgsOrigin = args map e - - // build a mapping for the function... - //val frame = rctx.withNewVars2((tfd.paramIds zip evArgs).toMap) - - val callResult = if (tfd.fd.annotations("extern") && ctx.classDir.isDefined) { - functionInvocation(tfd.fd, evArgsOrigin) - } else { - if((!tfd.hasBody && !rctx.mappings.isDefinedAt(tfd.id)) || tfd.body.exists(b => ExprOps.exists(e => e.isInstanceOf[Choose])(b))) { - functionInvocation(tfd.fd, evArgsOrigin) - } else { - val body = tfd.body.getOrElse(rctx.mappings(tfd.id)) - try { - val frame = rctx.withNewVars3((tfd.paramIds zip evArgsValues).toMap, (tfd.paramIds zip evArgsOrigin).toMap) - e(body)(frame, gctx) - } catch { - case e: Throwable => - //println("Error during execution of " + expr + ": " + e + ", " + e.getMessage) - //println(e.getStackTrace.map(_.toString).mkString("\n")) - functionInvocation(tfd.fd, evArgsOrigin) - } - } - } - callResult - - case Let(i, ex, b) => - val first = underlying.e(ex) - val second = e(ex) - e(b)(rctx.withNewVar(i, first, second), gctx) - - case Application(caller, args) => - val ecaller = underlying.e(caller) - val tcaller = e(caller) - val targs = args map e - val eargs = args map underlying.e - ecaller match { - case l @ Lambda(params, body) if evaluateApplications => - val mapping = (params map (_.id) zip eargs).toMap - val mappingAbs = (params map (_.id) zip targs).toMap - e(body)(rctx.withNewVars3(mapping, mappingAbs), gctx) - case _ => - Application(tcaller, targs) - } - - case l @ Lambda(_, _) => - import ExprOps._ - val mapping = variablesOf(l).map(id => id -> e(Variable(id))).toMap - replaceFromIDs(mapping, l).asInstanceOf[Lambda] - - case Operator(es, builder) => - val ees = es.map(underlying.e) - val ts = es.map(e) - val conc_value = underlying.e(builder(ees)) - val abs_value = builder(ts) - val final_abs_value = if( evaluateCaseClassSelector) { - abs_value match { - case CaseClassSelector(cct, CaseClass(ct, args), id) => - args(ct.classDef.selectorID2Index(id)) - case CaseClassSelector(cct, AsInstanceOf(CaseClass(ct, args), ccct), id) => - args(ct.classDef.selectorID2Index(id)) - case TupleSelect(Tuple(args), i) => - args(i-1) - case Assert(a, error, body) => body - case MapApply(FiniteMap(theMap, keyType, valueType), thekey) if theMap contains thekey => - theMap(thekey) - case e => e - } - } else abs_value - - final_abs_value - } - } - - def matchesCaseAbstractOnly(scrut: Expr, abstractScrut: Expr, caze: MatchCase)(implicit rctx: RC, gctx: GC): Option[(MatchCase, Iterable[(Identifier, Expr)])] = { - import purescala.TypeOps.isSubtypeOf - import purescala.Extractors._ - - def matchesPattern(pat: Pattern, expr: Expr, exprFromScrut: Expr): Option[Iterable[(Identifier, Expr)]] = (pat, expr) match { - case (InstanceOfPattern(ob, pct), _) => - if (isSubtypeOf(exprFromScrut.getType, pct)) { - Some(obind(ob, exprFromScrut/*AsInstanceOf(exprFromScrut, pct)*/)) // is AsInstanceOf needed? - } else { - None - } - case (WildcardPattern(ob), _) => - Some(obind(ob, exprFromScrut)) - - case (CaseClassPattern(ob, pct, subs), CaseClass(ct, args)) => - if (pct == ct) { - val res = (subs zip args zip ct.classDef.fieldsIds).map{ - case ((s, a), id) => - exprFromScrut match { - case CaseClass(ct, args) if evaluateCaseClassSelector => - matchesPattern(s, a, args(ct.classDef.selectorID2Index(id))) - case AsInstanceOf(CaseClass(ct, args), _) if evaluateCaseClassSelector => - matchesPattern(s, a, args(ct.classDef.selectorID2Index(id))) - case _ => - matchesPattern(s, a, CaseClassSelector(ct, exprFromScrut, id)) - } - } - if (res.forall(_.isDefined)) { - Some(obind(ob, exprFromScrut) ++ res.flatten.flatten) - } else { - None - } - } else { - None - } - case (up @ UnapplyPattern(ob, _, subs), scrut) => - underlying.e(functionInvocation(up.unapplyFun.fd, Seq(scrut))) match { - case CaseClass(CaseClassType(cd, _), Seq()) if cd == program.library.None.get => - None - case CaseClass(CaseClassType(cd, _), Seq(arg)) if cd == program.library.Some.get => - val res = (subs zip unwrapTuple(arg, subs.size)).zipWithIndex map { - case ((s, a), i) => matchesPattern(s, a, tupleSelect(arg, i + 1, subs.size)) - } - if (res.forall(_.isDefined)) { - Some(obind(ob, scrut) ++ res.flatten.flatten) - } else { - None - } - case other => - throw EvalError(typeErrorMsg(other, up.unapplyFun.returnType)) - } - case (TuplePattern(ob, subs), Tuple(args)) => - if (subs.size == args.size) { - val res = (subs zip args).zipWithIndex.map{ - case ((s, a), i) => - exprFromScrut match { - case Tuple(args) if evaluateCaseClassSelector=> - matchesPattern(s, a, args(i)) - case _ => - matchesPattern(s, a, TupleSelect(exprFromScrut, i+1)) - } - } - if (res.forall(_.isDefined)) { - Some(obind(ob, exprFromScrut) ++ res.flatten.flatten) - } else { - None - } - } else { - None - } - case (LiteralPattern(ob, l1) , l2 : Literal[_]) if l1 == l2 => - Some(obind(ob, exprFromScrut)) - case _ => None - } - - def obind(ob: Option[Identifier], eBuilder: Expr): Iterable[(Identifier, Expr)] = { - ob.map(id => id -> (eBuilder)).toList - } - - caze match { - case SimpleCase(p, rhs) => - matchesPattern(p, scrut, abstractScrut).map(r => - (caze, r) - ) - - case GuardedCase(p, g, rhs) => - for { - r <- matchesPattern(p, scrut, abstractScrut) - if underlying.e(g)(rctx.concMapping.withNewVars(r.map(kv => kv._1 -> underlying.e(kv._2)).toMap), gctx) == BooleanLiteral(true) - } yield (caze, r) - } - } -} diff --git a/src/main/scala/inox/evaluators/AngelicEvaluator.scala b/src/main/scala/inox/evaluators/AngelicEvaluator.scala deleted file mode 100644 index 26db1865b..000000000 --- a/src/main/scala/inox/evaluators/AngelicEvaluator.scala +++ /dev/null @@ -1,46 +0,0 @@ -/* Copyright 2009-2016 EPFL, Lausanne */ - -package leon -package evaluators - -import leon.solvers.Model -import purescala.Expressions.Expr -import EvaluationResults._ - -class AngelicEvaluator(underlying: NDEvaluator) - extends Evaluator(underlying.context, underlying.program) - with DeterministicEvaluator { - - val description: String = "angelic evaluator" - val name: String = "Interpreter that returns the first solution of a non-deterministic program" - - val bank = new EvaluationBank - - def eval(expr: Expr, model: Model): EvaluationResult = underlying.eval(expr, model) match { - case Successful(Stream(h, _*)) => - Successful(h) - case Successful(Stream()) => - RuntimeError("Underlying ND-evaluator returned no solution") - case other@(RuntimeError(_) | EvaluatorError(_)) => - other.asInstanceOf[Result[Nothing]] - } -} - -class DemonicEvaluator(underlying: NDEvaluator) - extends Evaluator(underlying.context, underlying.program) - with DeterministicEvaluator { - - val description: String = "demonic evaluator" - val name: String = "Interpreter that demands an underlying non-deterministic program has unique solution" - - val bank = new EvaluationBank - - def eval(expr: Expr, model: Model): EvaluationResult = underlying.eval(expr, model) match { - case Successful(Stream(h)) => - Successful(h) - case Successful(_) => - RuntimeError("Underlying ND-evaluator did not return unique solution!") - case other@(RuntimeError(_) | EvaluatorError(_)) => - other.asInstanceOf[Result[Nothing]] - } -} diff --git a/src/main/scala/inox/evaluators/CodeGenEvaluator.scala b/src/main/scala/inox/evaluators/CodeGenEvaluator.scala deleted file mode 100644 index 74df09bad..000000000 --- a/src/main/scala/inox/evaluators/CodeGenEvaluator.scala +++ /dev/null @@ -1,78 +0,0 @@ -/* Copyright 2009-2016 EPFL, Lausanne */ - -package leon -package evaluators - -import purescala.Common._ -import purescala.Definitions._ -import purescala.Expressions._ - -import codegen.CompilationUnit -import codegen.CompiledExpression -import codegen.CodeGenParams - -import leon.codegen.runtime.LeonCodeGenRuntimeException -import leon.codegen.runtime.LeonCodeGenEvaluationException - -class CodeGenEvaluator(ctx: LeonContext, val unit: CompilationUnit) extends Evaluator(ctx, unit.program) with DeterministicEvaluator { - - val name = "codegen-eval" - val description = "Evaluator for PureScala expressions based on compilation to JVM" - - val bank = unit.bank - - /** Another constructor to make it look more like other `Evaluator`s. */ - def this(ctx: LeonContext, prog: Program, bank: EvaluationBank = new EvaluationBank, params: CodeGenParams = CodeGenParams.default) { - this(ctx, new CompilationUnit(ctx, prog, bank, params)) - } - - private def compileExpr(expression: Expr, args: Seq[Identifier]): Option[CompiledExpression] = { - ctx.timers.evaluators.codegen.compilation.start() - try { - Some(unit.compileExpression(expression, args)(ctx)) - } catch { - case t: Throwable => - ctx.reporter.warning(expression.getPos, "Error while compiling expression: "+t.getMessage) - None - } finally { - ctx.timers.evaluators.codegen.compilation.stop() - } - } - - def eval(expression: Expr, model: solvers.Model) : EvaluationResult = { - compile(expression, model.toSeq.map(_._1)).map { e => - ctx.timers.evaluators.codegen.runtime.start() - val res = e(model) - ctx.timers.evaluators.codegen.runtime.stop() - res - }.getOrElse(EvaluationResults.EvaluatorError("Couldn't compile expression.")) - } - - override def compile(expression: Expr, args: Seq[Identifier]) : Option[solvers.Model=>EvaluationResult] = { - compileExpr(expression, args).map(ce => (model: solvers.Model) => { - if (args.exists(arg => !model.isDefinedAt(arg))) { - EvaluationResults.EvaluatorError("Model undefined for free arguments") - } else try { - EvaluationResults.Successful(ce.eval(model)) - } catch { - case e : ArithmeticException => - EvaluationResults.RuntimeError(e.getMessage) - - case e : ArrayIndexOutOfBoundsException => - EvaluationResults.RuntimeError(e.getMessage) - - case e : LeonCodeGenRuntimeException => - EvaluationResults.RuntimeError(e.getMessage) - - case e : LeonCodeGenEvaluationException => - EvaluationResults.EvaluatorError(e.getMessage) - - case e : java.lang.ExceptionInInitializerError => - EvaluationResults.RuntimeError(e.getException.getMessage) - - case so : java.lang.StackOverflowError => - EvaluationResults.RuntimeError("Stack overflow") - } - }) - } - } diff --git a/src/main/scala/inox/evaluators/DualEvaluator.scala b/src/main/scala/inox/evaluators/DualEvaluator.scala deleted file mode 100644 index a99f4d477..000000000 --- a/src/main/scala/inox/evaluators/DualEvaluator.scala +++ /dev/null @@ -1,138 +0,0 @@ -/* Copyright 2009-2016 EPFL, Lausanne */ - -package leon -package evaluators - -import purescala.Common._ -import purescala.Expressions._ -import purescala.Definitions._ -import purescala.Types._ - -import codegen._ -import codegen.runtime.{StdMonitor, Monitor} - -class DualEvaluator( - ctx: LeonContext, - prog: Program, - bank: EvaluationBank = new EvaluationBank, - params: CodeGenParams = CodeGenParams.default -) extends RecursiveEvaluator(ctx, prog, params.maxFunctionInvocations) - with HasDefaultGlobalContext { - - type RC = DualRecContext - def initRC(mappings: Map[Identifier, Expr]): RC = DualRecContext(mappings) - implicit val debugSection = utils.DebugSectionEvaluation - - val unit = new CompilationUnit(ctx, prog, bank, params) - - var monitor: Monitor = new StdMonitor(unit, params.maxFunctionInvocations, Map()) - - val isCompiled = prog.definedFunctions.toSet - - case class DualRecContext(mappings: Map[Identifier, Expr], needJVMRef: Boolean = false) extends RecContext[DualRecContext] { - def newVars(news: Map[Identifier, Expr]) = copy(news) - } - - case class RawObject(o: AnyRef, tpe: TypeTree) extends Expr { - val getType = tpe - } - - def call(tfd: TypedFunDef, args: Seq[AnyRef]): Expr = { - - val (className, methodName, _) = unit.leonFunDefToJVMInfo(tfd.fd).get - - val allArgs = Seq(monitor) ++ - (if (tfd.fd.tparams.nonEmpty) Seq(tfd.tps.map(unit.registerType(_)).toArray) else Seq()) ++ - args - - ctx.reporter.debug(s"Calling $className.$methodName(${args.mkString(",")})") - - try { - val cl = unit.loader.loadClass(className) - - val meth = cl.getMethods.find(_.getName == methodName).get - - val res = if (allArgs.isEmpty) { - meth.invoke(null) - } else { - meth.invoke(null, allArgs : _*) - } - - RawObject(res, tfd.returnType) - } catch { - case e: java.lang.reflect.InvocationTargetException => - throw new RuntimeError(e.getCause.getMessage) - - case t: Throwable => - t.printStackTrace() - throw new EvalError(t.getMessage) - } - } - - def retrieveField(fd : FunDef): Expr = { - - val (className, fieldName, _) = unit.leonFunDefToJVMInfo(fd).get - - ctx.reporter.debug(s"Retrieving $className.$fieldName") - - try { - val cl = unit.loader.loadClass(className) - - val field = cl.getFields.find(_.getName == fieldName).get - - val res = field.get(null) - - RawObject(res, fd.returnType) - } catch { - case e: java.lang.reflect.InvocationTargetException => - throw new RuntimeError(e.getCause.getMessage) - - case t: Throwable => - t.printStackTrace() - throw new EvalError(t.getMessage) - } - } - - - - override def e(expr: Expr)(implicit rctx: RC, gctx: GC): Expr = expr match { - case FunctionInvocation(tfd, args) => - if (isCompiled(tfd.fd)) { - if (!tfd.fd.canBeStrictField) { - val rargs = args.map( - e(_)(rctx.copy(needJVMRef = true), gctx) match { - case RawObject(obj, _) => obj - case _ => throw new EvalError("Failed to get JVM ref when requested") - } - ) - - jvmBarrier(call(tfd, rargs), rctx.needJVMRef) - } else { - jvmBarrier(retrieveField(tfd.fd), rctx.needJVMRef) - } - } else { - jvmBarrier(super.e(expr)(rctx.copy(needJVMRef = false), gctx), rctx.needJVMRef) - } - case _ => - jvmBarrier(super.e(expr)(rctx.copy(needJVMRef = false), gctx), rctx.needJVMRef) - } - - def jvmBarrier(e: Expr, returnJVMRef: Boolean): Expr = { - e match { - case RawObject(obj, _) if returnJVMRef => - e - case RawObject(obj, _) if !returnJVMRef => - unit.jvmToValue(obj, e.getType) - case e if returnJVMRef => - RawObject(unit.valueToJVM(e)(monitor), e.getType) - case e if !returnJVMRef => - e - } - } - - override def eval(ex: Expr, model: solvers.Model) = { - monitor = unit.getMonitor(model, params.maxFunctionInvocations) - super.eval(ex, model) - } - -} diff --git a/src/main/scala/inox/evaluators/EvaluationPhase.scala b/src/main/scala/inox/evaluators/EvaluationPhase.scala deleted file mode 100644 index a06d1b39a..000000000 --- a/src/main/scala/inox/evaluators/EvaluationPhase.scala +++ /dev/null @@ -1,55 +0,0 @@ -/* Copyright 2009-2016 EPFL, Lausanne */ - -package leon -package evaluators - -import leon.Main.MainComponent -import purescala.Definitions._ -import purescala.DefOps._ -import purescala.Expressions._ - -object EvaluationPhase extends UnitPhase[Program] { - val name = "Evaluation" - val description = "Evaluating ground functions" - - implicit val debugSection = utils.DebugSectionEvaluation - - def apply(ctx: LeonContext, program: Program): Unit = { - val evalFuns: Option[Seq[String]] = ctx.findOption(GlobalOptions.optFunctions) - - val evaluator = ctx.findOptionOrDefault(MainComponent.optEval) - - val reporter = ctx.reporter - - val fdFilter = { - import OptionsHelpers._ - - filterInclusive(evalFuns.map(fdMatcher(program)), None) - } - - val toEvaluate = funDefsFromMain(program).toList.filter(_.params.size == 0).filter(fdFilter).sortWith((fd1, fd2) => fd1.getPos < fd2.getPos) - - if (toEvaluate.isEmpty) reporter.warning("No ground functions found with the given names") - - val eval = if (evaluator == "codegen") { - new CodeGenEvaluator(ctx, program) - } else if (evaluator == "default" || evaluator == "") { - new DefaultEvaluator(ctx, program) - } else { - reporter.fatalError(s"Unknown evaluator '$evaluator'. Available: default, codegen") - } - - for (fd <- toEvaluate) { - reporter.info(s" - Evaluating ${fd.id}") - val call = FunctionInvocation(fd.typed, Seq()) - eval.eval(call) match { - case EvaluationResults.Successful(ex) => - reporter.info(s" => $ex") - case EvaluationResults.RuntimeError(msg) => - reporter.warning(s" Runtime Error: $msg") - case EvaluationResults.EvaluatorError(msg) => - reporter.warning(s" Evaluator Error: $msg") - } - } - } -} diff --git a/src/main/scala/inox/evaluators/ScalacEvaluator.scala b/src/main/scala/inox/evaluators/ScalacEvaluator.scala deleted file mode 100644 index db855fbcd..000000000 --- a/src/main/scala/inox/evaluators/ScalacEvaluator.scala +++ /dev/null @@ -1,459 +0,0 @@ -/* Copyright 2009-2016 EPFL, Lausanne */ - -package leon -package evaluators - -import leon.codegen.runtime.RuntimeResources - -import purescala.Definitions._ -import purescala.DefOps._ -import purescala.Expressions._ -import purescala.Types._ - -import java.io.File -import java.net.URLClassLoader -import java.lang.reflect.Constructor - -/** - * Call scalac-compiled functions from within Leon - * - * Known limitations: - * - Multiple argument lists - */ -class ScalacEvaluator(ev: DeterministicEvaluator, ctx: LeonContext, pgm: Program) extends LeonComponent { - implicit val _: Program = pgm - - val name = "Evaluator of external functions" - val description = "Evaluator for non-purescala functions" - - implicit val debugSection = utils.DebugSectionEvaluation - - val classPaths: List[File] = ctx.classDir.toList - - lazy val runtimeTok = { - RuntimeResources.register(this) - } - - private def unsupported(err: String): Nothing = { - throw new RuntimeException(err) - } - - private def encodeName(name: String): String = { - scala.reflect.NameTransformer.encode(name) - } - - private def jvmName(d: Definition): String = { - compiledName(d).replace(".", "/") - } - - private def jvmClassName(tpe: TypeTree): String = tpe match { - case IntegerType => - "scala/math/BigInt" - - case TupleType(subs) => - f"scala/Tuple${subs.size}%d" - - case UnitType => - "scala/runtime/BoxedUnit" - - case ct: ClassType => - jvmName(ct.classDef) - - case _ => - ctx.reporter.internalError(s"$tpe is not a jvm class type ?!?") - } - - private def compiledName(d: Definition): String = { - // Scala encodes fullname of modules using ".". After the module, it - // becomes an inner class, with '$' - val path = pathFromRoot(d) - - val pathToModule = path.takeWhile{!_.isInstanceOf[ModuleDef]} - val rest = path.drop(pathToModule.size) - - val upToModule = pathToNames(pathToModule, false).map(encodeName).mkString(".") - - if(rest.isEmpty) { - upToModule - } else { - d match { - case md: ModuleDef => - upToModule+"."+encodeName(md.id.name)+"$" - case _ => - val afterModule = pathToNames(rest, false).map(encodeName).mkString("$") - upToModule+"."+afterModule - } - } - } - - def call(tfd: TypedFunDef, args: Seq[Expr]): Expr = { - val fd = tfd.fd - - val methodName = encodeName(fd.id.name) - - val jvmArgs = args.map(leonToScalac) - - val (clazz, jvmRec, jvmCallArgs) = fd.methodOwner match { - case Some(cd) => - (cd, jvmArgs.head, jvmArgs.tail) - - case None => - val md = moduleOf(fd).get - - val jvmModule = loadClass(compiledName(md)) - val rec = jvmModule.getField("MODULE$").get(null) - - (md, rec, jvmArgs) - } - - val jvmClassName = compiledName(clazz) - val jvmClass = loadClass(jvmClassName) - - ctx.reporter.debug(s"Calling $jvmClassName.${tfd.signature}(${args.mkString(", ")})") - - // Lookup method in jvmClass - - jvmClass.getMethods().filter(_.getName() == methodName).toList match { - case List(meth) => - - val res = if (jvmCallArgs.isEmpty) { - meth.invoke(jvmRec) - } else { - meth.invoke(jvmRec, jvmCallArgs: _*) - } - - scalacToLeon(res, tfd.returnType) - - case Nil => - unsupported(s"Unable to lookup method") - - case ms => - unsupported(s"Ambiguous reference to method: ${ms.size} methods found with a matching name") - - } - } - - val localClassLoader = { - classOf[ScalacEvaluator].getClassLoader() - } - - val toInstrument = { - val fds = (for (fd <- pgm.definedFunctions if !fd.annotations("extern")) yield { - encodeName(fd.id.name) -> fd - }).toMap - - fds.groupBy { case (n, fd) => - compiledName(fd.methodOwner.getOrElse(moduleOf(fd).get)) - } - } - - val compiledClassLoader = { - val classUrls = ctx.classDir.map(_.toURI.toURL) - val cl = new URLClassLoader(classUrls.toArray, localClassLoader) - new InterceptingClassLoader(cl) - } - - def loadClass(classname: String): Class[_] = { - compiledClassLoader.loadClass(classname) - } - - def leonToScalac(e: Expr): AnyRef = e match { - case CharLiteral(v) => - new java.lang.Character(v) - - case IntLiteral(v) => - new java.lang.Integer(v) - - case InfiniteIntegerLiteral(v) => - v - - case BooleanLiteral(v) => - new java.lang.Boolean(v) - - case Tuple(exprs) => - val name = f"scala.Tuple${exprs.size}%d" - - val cl = localClassLoader.loadClass(name) - val constr = cl.getConstructors().head.asInstanceOf[Constructor[AnyRef]] - - constr.newInstance(exprs.map(leonToScalac) : _*) - - case UnitLiteral() => - Unit.box(()) - - case FiniteSet(exprs, tpe) => - - val cl = compiledClassLoader.loadClass("leon.lang.Set") - val constr = cl.getConstructors().head.asInstanceOf[Constructor[AnyRef]] - - constr.newInstance(Set(exprs.toSeq.map(leonToScalac) : _*)) - - case FiniteMap(pairs, ktpe, vtpe) => - - val cl = compiledClassLoader.loadClass("leon.lang.Map") - val constr = cl.getConstructors().head.asInstanceOf[Constructor[AnyRef]] - - constr.newInstance(Map( - pairs.map { case (k, v) => - leonToScalac(k) -> leonToScalac(v) - }.toSeq : _* - )) - - case CaseClass(cct, fields) => - - val name = compiledName(cct.classDef) - - val cl = loadClass(name) - val constr = cl.getConstructors().head.asInstanceOf[Constructor[AnyRef]] - constr.newInstance(fields.map(leonToScalac) : _*) - - - case Lambda(_, _) => - unsupported("It is not yet possible to pass a closure to an @extern function") - - case t: Terminal => - unsupported("Unhandled conversion to scala runtime: "+t) - - case _ => - unsupported("Trying to convert non-terminal: "+e) - } - - def productToElems(p: Product, tps: Seq[TypeTree]): Seq[Expr] = { - for ((t,i) <- tps.zipWithIndex) yield { - scalacToLeon(p.productElement(i), t) - } - } - - def scalacToLeon(o: Any, t: TypeTree): Expr = t match { - case BooleanType => - BooleanLiteral(o.asInstanceOf[Boolean].booleanValue) - case Int32Type => - IntLiteral(o.asInstanceOf[Integer].intValue) - case CharType => - CharLiteral(o.asInstanceOf[Character].charValue) - case IntegerType => - InfiniteIntegerLiteral(o.asInstanceOf[BigInt]) - case UnitType => - UnitLiteral() - case TupleType(tps) => - Tuple(productToElems(o.asInstanceOf[Product], tps)) - case cct: CaseClassType => - CaseClass(cct, productToElems(o.asInstanceOf[Product], cct.fieldsTypes)) - - case act: AbstractClassType => - val className = o.getClass.getName - - act.knownCCDescendants.find(cct => compiledName(cct.classDef) == className) match { - case Some(cct) => - scalacToLeon(o, cct) - - case None => - unsupported("Expected "+act+", got: "+className) - } - - case SetType(b) => - val cl = compiledClassLoader.loadClass("leon.lang.Set") - - val s = cl.getMethod("theSet").invoke(o).asInstanceOf[Set[_]] - - FiniteSet(s.iterator.map(scalacToLeon(_, b)).toSet, b) - - case MapType(ktpe, vtpe) => - - val cl = compiledClassLoader.loadClass("leon.lang.Map") - - val s = cl.getMethod("theMap").invoke(o).asInstanceOf[Map[_, _]] - - FiniteMap(s.iterator.map { - case (k, v) => scalacToLeon(k, ktpe) -> scalacToLeon(v, vtpe) - }.toMap, ktpe, vtpe) - - case FunctionType(_, _) => - unsupported("It is not possible to pass a closure from @extern back to leon") - - case _ => - unsupported("Unhandled conversion from scala runtime: "+t) - } - - def jvmCallBack(cName: String, fName: String, args: Array[AnyRef]): AnyRef = { - toInstrument.get(cName).flatMap(_.get(fName)) match { - case Some(fd) => - val leonArgs = fd.params.map(_.getType).zip(args).map { - case (tpe, arg) => scalacToLeon(arg, tpe) - } - - val fi = FunctionInvocation(fd.typed, leonArgs) - leonToScalac(ev.eval(fi).result.getOrElse { - unsupported("Evaluation in Leon failed!") - }) - - case None => - unsupported("Unable to locate callback function "+cName+"."+fName) - } - } - - /** - * Black magic here we come! - */ - import org.objectweb.asm.ClassReader - import org.objectweb.asm.ClassWriter - import org.objectweb.asm.ClassVisitor - import org.objectweb.asm.MethodVisitor - import org.objectweb.asm.Opcodes - - class InterceptingClassLoader(p: ClassLoader) extends ClassLoader(p) { - import java.io._ - - var loaded = Set[String]() - - override def loadClass(name: String, resolve: Boolean): Class[_] = { - if (loaded(name)) { - super.loadClass(name, resolve) - } else { - loaded += name - - if (!(toInstrument contains name)) { - super.loadClass(name, resolve) - } else { - val bs = new ByteArrayOutputStream() - val is = getResourceAsStream(name.replace('.', '/') + ".class") - val buf = new Array[Byte](512) - var len = is.read(buf) - while (len > 0) { - bs.write(buf, 0, len) - len = is.read(buf) - } - - // Transform bytecode - val cr = new ClassReader(bs.toByteArray()) - val cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES) - val cv = new LeonCallsClassVisitor(cw, name, toInstrument(name)) - cr.accept(cv, 0) - - val res = cw.toByteArray() - - defineClass(name, res, 0, res.length) - } - } - } - } - - class LeonCallsClassVisitor(cv: ClassVisitor, cName: String, fdMap: Map[String, FunDef]) extends ClassVisitor(Opcodes.ASM5, cv) { - override def visitMethod(access: Int, fName: String, desc: String, sig: String, exceptions: Array[String]) = { - val mv = super.visitMethod(access, fName, desc, sig, exceptions) - if ((fdMap contains fName)) { - new LeonCallsMethodVisitor(mv, cName, fName, fdMap(fName)) - } else { - mv - } - } - } - - class LeonCallsMethodVisitor(mv: MethodVisitor, cName: String, fName: String, fd: FunDef) extends MethodVisitor(Opcodes.ASM5, mv) { - - def box(tpe: TypeTree) = tpe match { - case Int32Type => - mv.visitTypeInsn(Opcodes.NEW, "java/lang/Integer") - mv.visitInsn(Opcodes.DUP_X1) - mv.visitInsn(Opcodes.SWAP) - mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Integer", "<init>", "(I)V", false) - - case CharType => - mv.visitTypeInsn(Opcodes.NEW, "java/lang/Character") - mv.visitInsn(Opcodes.DUP_X1) - mv.visitInsn(Opcodes.SWAP) - mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Character", "<init>", "(C)V", false) - - case BooleanType => - mv.visitTypeInsn(Opcodes.NEW, "java/lang/Boolean") - mv.visitInsn(Opcodes.DUP_X1) - mv.visitInsn(Opcodes.SWAP) - mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Boolean", "<init>", "(Z)V", false) - - case _ => - } - - def unbox(tpe: TypeTree) = tpe match { - case Int32Type => - mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Integer") - mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false) - - case CharType => - mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Character") - mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C", false) - - case BooleanType => - mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Boolean") - mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z", false) - - case tpe => - mv.visitTypeInsn(Opcodes.CHECKCAST, jvmClassName(tpe)) - } - - def loadOpCode(tpe: TypeTree) = tpe match { - case CharType => Opcodes.ILOAD - case Int32Type => Opcodes.ILOAD - case BooleanType => Opcodes.ILOAD - case _ => Opcodes.ALOAD - } - - def returnOpCode(tpe: TypeTree) = tpe match { - case CharType => Opcodes.IRETURN - case Int32Type => Opcodes.IRETURN - case BooleanType => Opcodes.IRETURN - case UnitType => Opcodes.RETURN - case _ => Opcodes.ARETURN - } - - - override def visitCode() { - mv.visitLdcInsn(runtimeTok.id) - mv.visitLdcInsn(cName) - mv.visitLdcInsn(fName) - - // If we are visiting a method, we need to add the receiver in the arguments array - val readOffset = fd.methodOwner match { - case Some(cd) => - // $this is the receiver - 0 - case _ => - // We skip the module object - 1 - } - - // Array of arguments called - mv.visitLdcInsn(fd.params.size) - mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object") - - // Note: fd.params includes the receiver! - // Add actual arguments, box necessary values - for ((tpe, i) <- fd.params.map(_.getType).zipWithIndex) { - // Array ref - mv.visitInsn(Opcodes.DUP) - mv.visitLdcInsn(i) - // Arg Value - mv.visitVarInsn(loadOpCode(tpe), i+readOffset) - box(tpe) - mv.visitInsn(Opcodes.AASTORE) - } - - - mv.visitMethodInsn(Opcodes.INVOKESTATIC, - "leon/evaluators/LeonJVMCallBacks", - "callBack", - "(ILjava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/Object;", - false) - - unbox(fd.returnType) - mv.visitInsn(returnOpCode(fd.returnType)) - mv.visitEnd() - } - } -} - -object LeonJVMCallBacks { - def callBack(token: Int, className: String, methodName: String, args: Array[AnyRef]): AnyRef = { - val ev = RuntimeResources.get[ScalacEvaluator](token) - ev.jvmCallBack(className, methodName, args) - } -} diff --git a/src/main/scala/inox/evaluators/StreamEvaluator.scala b/src/main/scala/inox/evaluators/StreamEvaluator.scala deleted file mode 100644 index 8caf0c9f4..000000000 --- a/src/main/scala/inox/evaluators/StreamEvaluator.scala +++ /dev/null @@ -1,718 +0,0 @@ -/* Copyright 2009-2016 EPFL, Lausanne */ - -package leon -package evaluators - -import purescala.Constructors._ -import purescala.ExprOps._ -import purescala.Extractors._ -import purescala.TypeOps.{leastUpperBound, isSubtypeOf} -import purescala.Types._ -import purescala.Common.Identifier -import purescala.Definitions.{TypedFunDef, Program} -import purescala.Expressions._ -import purescala.Quantification._ - -import leon.solvers.{SolverFactory, PartialModel} -import leon.solvers.unrolling.UnrollingProcedure -import leon.utils.StreamUtils._ - -import scala.concurrent.duration._ - -class StreamEvaluator(ctx: LeonContext, prog: Program) - extends ContextualEvaluator(ctx, prog, 50000) - with NDEvaluator - with HasDefaultGlobalContext - with HasDefaultRecContext -{ - - val name = "ND-evaluator" - val description = "Non-deterministic interpreter for Leon programs that returns a Stream of solutions" - - protected[evaluators] def e(expr: Expr)(implicit rctx: RC, gctx: GC): Stream[Expr] = expr match { - case Variable(id) => - rctx.mappings.get(id).toStream - - case Application(caller, args) => - for { - cl <- e(caller).distinct - newArgs <- cartesianProduct(args map e).distinct - res <- cl match { - case l @ Lambda(params, body) => - val mapping = l.paramSubst(newArgs) - e(body)(rctx.withNewVars(mapping), gctx).distinct - case FiniteLambda(mapping, dflt, _) => - // FIXME - Stream(mapping.collectFirst { - case (pargs, res) if (newArgs zip pargs).forall { case (f, r) => f == r } => - res - }.getOrElse(dflt)) - case _ => - Stream() - } - } yield res - - case Let(i,v,b) => - for { - ev <- e(v).distinct - eb <- e(b)(rctx.withNewVar(i, ev), gctx).distinct - } yield eb - - case Assert(cond, oerr, body) => - e(IfExpr(Not(cond), Error(expr.getType, oerr.getOrElse("Assertion failed @"+expr.getPos)), body)) - - case en@Ensuring(body, post) => - e(en.toAssert) - - case Error(tpe, desc) => - Stream() - - case IfExpr(cond, thenn, elze) => - e(cond).distinct.flatMap { - case BooleanLiteral(true) => e(thenn) - case BooleanLiteral(false) => e(elze) - case other => Stream() - }.distinct - - case FunctionInvocation(TypedFunDef(fd, Seq(tp)), Seq(set)) if fd == program.library.setToList.get => - val cons = program.library.Cons.get - val nil = CaseClass(CaseClassType(program.library.Nil.get, Seq(tp)), Seq()) - def mkCons(h: Expr, t: Expr) = CaseClass(CaseClassType(cons, Seq(tp)), Seq(h,t)) - e(set).distinct.collect { - case FiniteSet(els, _) => - els.foldRight(nil)(mkCons) - }.distinct - - case FunctionInvocation(tfd, args) => - if (gctx.stepsLeft < 0) { - return Stream() - } - gctx.stepsLeft -= 1 - - for { - evArgs <- cartesianProduct(args map e).distinct - // build a mapping for the function... - frame = rctx.withNewVars(tfd.paramSubst(evArgs)) - BooleanLiteral(true) <- e(tfd.precOrTrue)(frame, gctx).distinct - body <- tfd.body.orElse(rctx.mappings.get(tfd.id)).toStream - callResult <- e(body)(frame, gctx).distinct - BooleanLiteral(true) <- e(application(tfd.postOrTrue, Seq(callResult)))(frame, gctx).distinct - } yield callResult - - case And(args) if args.isEmpty => - Stream(BooleanLiteral(true)) - - case And(args) => - e(args.head).distinct.flatMap { - case BooleanLiteral(false) => Stream(BooleanLiteral(false)) - case BooleanLiteral(true) => e(andJoin(args.tail)) - case other => Stream() - } - - case Or(args) if args.isEmpty => - Stream(BooleanLiteral(false)) - - case Or(args) => - e(args.head).distinct.flatMap { - case BooleanLiteral(true) => Stream(BooleanLiteral(true)) - case BooleanLiteral(false) => e(orJoin(args.tail)) - case other => Stream() - } - - case Implies(lhs, rhs) => - e(Or(Not(lhs), rhs)) - - case l @ Lambda(_, _) => - val mapping = variablesOf(l).map(id => - id -> (e(Variable(id)) match { - case Stream(v) => v - case _ => return Stream() - }) - ).toMap - Stream(replaceFromIDs(mapping, l)) - - case fl @ FiniteLambda(mapping, dflt, tpe) => - // finite lambda should always be ground! - Stream(fl) - - case f @ Forall(fargs, body) => - - // TODO add memoization - implicit val debugSection = utils.DebugSectionVerification - - ctx.reporter.debug("Executing forall!") - - val mapping = variablesOf(f).map(id => id -> rctx.mappings(id)).toMap - val context = mapping.toSeq.sortBy(_._1.uniqueName).map(_._2) - - val tStart = System.currentTimeMillis - - val newCtx = ctx.copy(options = ctx.options.map { - case LeonOption(optDef, value) if optDef == UnrollingProcedure.optFeelingLucky => - LeonOption(optDef)(false) - case opt => opt - }) - - val solverf = SolverFactory.getFromSettings(newCtx, program).withTimeout(1.second) - val solver = solverf.getNewSolver() - - try { - val cnstr = Not(replaceFromIDs(mapping, body)) - solver.assertCnstr(cnstr) - - gctx.model match { - case pm: PartialModel => - val quantifiers = fargs.map(_.id).toSet - val quorums = extractQuorums(body, quantifiers) - - val domainCnstr = orJoin(quorums.map { quorum => - val quantifierDomains = quorum.flatMap { case (path, caller, args) => - val optMatcher = e(caller) match { - case Stream(l: Lambda) => Some(gctx.lambdas.getOrElse(l, l)) - case Stream(ev) => Some(ev) - case _ => None - } - - optMatcher.toSeq.flatMap { matcher => - val domain = pm.domains.get(matcher) - args.zipWithIndex.flatMap { - case (Variable(id),idx) if quantifiers(id) => - Some(id -> domain.map(cargs => path -> cargs(idx))) - case _ => None - } - } - } - - val domainMap = quantifierDomains.groupBy(_._1).mapValues(_.map(_._2).flatten) - andJoin(domainMap.toSeq.map { case (id, dom) => - orJoin(dom.toSeq.map { case (path, value) => - // @nv: Note that equality is allowed here because of first-order quantifiers. - // See [[leon.codegen.runtime.Monitor]] for more details. - path and Equals(Variable(id), value) - }) - }) - }) - - solver.assertCnstr(domainCnstr) - - case _ => - } - - solver.check match { - case Some(negRes) => - val total = System.currentTimeMillis-tStart - val res = BooleanLiteral(!negRes) - ctx.reporter.debug("Verification took "+total+"ms") - ctx.reporter.debug("Finished forall evaluation with: "+res) - Stream(res) - case _ => - Stream() - } - } catch { - case e: Throwable => Stream() - } finally { - solverf.reclaim(solver) - solverf.shutdown() - } - - case p : Passes => - e(p.asConstraint) - - case choose: Choose => - - // TODO add memoization - implicit val debugSection = utils.DebugSectionSynthesis - - val p = synthesis.Problem.fromSpec(choose.pred) - - ctx.reporter.debug("Executing choose!") - - val tStart = System.currentTimeMillis - - val newCtx = ctx.copy(options = ctx.options.map { - case LeonOption(optDef, value) if optDef == UnrollingProcedure.optFeelingLucky => - LeonOption(optDef)(false) - case opt => opt - }) - - val solverf = SolverFactory.getFromSettings(newCtx, program) - val solver = solverf.getNewSolver() - - try { - val eqs = p.as.map { - case id => Equals(Variable(id), rctx.mappings(id)) - } - - val cnstr = p.pc withBindings p.as.map(id => id -> rctx.mappings(id)) and p.phi - solver.assertCnstr(cnstr) - - def getSolution = try { - solver.check match { - case Some(true) => - val model = solver.getModel - - val valModel = valuateWithModel(model) _ - - val res = p.xs.map(valModel) - val leonRes = tupleWrap(res) - val total = System.currentTimeMillis - tStart - - ctx.reporter.debug("Synthesis took " + total + "ms") - ctx.reporter.debug("Finished synthesis with " + leonRes.asString) - - Some(leonRes) - case _ => - None - } - } catch { - case _: Throwable => None - } - - Stream.iterate(getSolution)(prev => { - val ensureDistinct = Not(Equals(tupleWrap(p.xs.map{ _.toVariable}), prev.get)) - solver.assertCnstr(ensureDistinct) - val sol = getSolution - // Clean up when the stream ends - if (sol.isEmpty) { - solverf.reclaim(solver) - solverf.shutdown() - } - sol - }).takeWhile(_.isDefined).take(10).map(_.get) - // This take(10) is there because we are not working well with infinite streams yet... - } catch { - case e: Throwable => - solverf.reclaim(solver) - solverf.shutdown() - Stream() - } - - case synthesis.utils.MutableExpr(ex) => - e(ex) - - case MatchExpr(scrut, cases) => - - def matchesCase(scrut: Expr, caze: MatchCase)(implicit rctx: RC, gctx: GC): Stream[(MatchCase, Map[Identifier, Expr])] = { - import purescala.TypeOps.isSubtypeOf - - def matchesPattern(pat: Pattern, expr: Expr): Stream[Map[Identifier, Expr]] = (pat, expr) match { - case (InstanceOfPattern(ob, pct), e) => - (if (isSubtypeOf(e.getType, pct)) { - Some(obind(ob, e)) - } else { - None - }).toStream - case (WildcardPattern(ob), e) => - Stream(obind(ob, e)) - - case (CaseClassPattern(ob, pct, subs), CaseClass(ct, args)) => - if (pct == ct) { - val subMaps = (subs zip args).map{ case (s, a) => matchesPattern(s, a) } - cartesianProduct(subMaps).map( _.flatten.toMap ++ obind(ob, expr)) - } else { - Stream() - } - case (UnapplyPattern(ob, unapplyFun, subs), scrut) => - e(functionInvocation(unapplyFun.fd, Seq(scrut))) flatMap { - case CaseClass(CaseClassType(cd, _), Seq()) if cd == program.library.None.get => - None - case CaseClass(CaseClassType(cd, _), Seq(arg)) if cd == program.library.Some.get => - val subMaps = subs zip unwrapTuple(arg, subs.size) map { - case (s,a) => matchesPattern(s,a) - } - cartesianProduct(subMaps).map( _.flatten.toMap ++ obind(ob, expr)) - case other => - None - } - case (TuplePattern(ob, subs), Tuple(args)) => - if (subs.size == args.size) { - val subMaps = (subs zip args).map { case (s, a) => matchesPattern(s, a) } - cartesianProduct(subMaps).map(_.flatten.toMap ++ obind(ob, expr)) - } else Stream() - case (LiteralPattern(ob, l1) , l2 : Literal[_]) if l1 == l2 => - Stream(obind(ob,l1)) - case _ => Stream() - } - - def obind(ob: Option[Identifier], e: Expr): Map[Identifier, Expr] = { - Map[Identifier, Expr]() ++ ob.map(id => id -> e) - } - - caze match { - case SimpleCase(p, rhs) => - matchesPattern(p, scrut).map(r => - (caze, r) - ) - - case GuardedCase(p, g, rhs) => - for { - r <- matchesPattern(p, scrut) - BooleanLiteral(true) <- e(g)(rctx.withNewVars(r), gctx) - } yield (caze, r) - } - } - - for { - rscrut <- e(scrut) - cs <- cases.toStream.map(c => matchesCase(rscrut, c)).find(_.nonEmpty).toStream - (c, mp) <- cs - res <- e(c.rhs)(rctx.withNewVars(mp), gctx) - } yield res - - case Operator(es, _) => - cartesianProduct(es map e) flatMap { es => - try { - Some(step(expr, es)) - } catch { - case _: RuntimeError => - // EvalErrors stop the computation altogether - None - } - } - - case other => - context.reporter.error(other.getPos, "Error: don't know how to handle " + other.asString + " in Evaluator ("+other.getClass+").") - Stream() - } - - - protected def step(expr: Expr, subs: Seq[Expr])(implicit rctx: RC, gctx: GC): Expr = (expr, subs) match { - case (Tuple(_), ts) => - Tuple(ts) - - case (TupleSelect(_, i), rs) => - rs(i - 1) - - case (Assert(_, oerr, _), Seq(BooleanLiteral(cond), body)) => - if (cond) body - else throw RuntimeError(oerr.getOrElse("Assertion failed @" + expr.getPos)) - - case (Error(_, desc), _) => - throw RuntimeError("Error reached in evaluation: " + desc) - - case (FunctionInvocation(TypedFunDef(fd, Seq(tp)), _), Seq(FiniteSet(els, _))) if fd == program.library.setToList.get => - val cons = program.library.Cons.get - val nil = CaseClass(CaseClassType(program.library.Nil.get, Seq(tp)), Seq()) - def mkCons(h: Expr, t: Expr) = CaseClass(CaseClassType(cons, Seq(tp)), Seq(h, t)) - els.foldRight(nil)(mkCons) - - case (Not(_), Seq(BooleanLiteral(arg))) => - BooleanLiteral(!arg) - - case (Implies(_, _) Seq (BooleanLiteral(b1), BooleanLiteral(b2))) => - BooleanLiteral(!b1 || b2) - - case (Equals(_, _), Seq(lv, rv)) => - (lv, rv) match { - case (FiniteSet(el1, _), FiniteSet(el2, _)) => BooleanLiteral(el1 == el2) - case (FiniteBag(el1, _), FiniteBag(el2, _)) => BooleanLiteral(el1 == el2) - case (FiniteMap(el1, _, _), FiniteMap(el2, _, _)) => BooleanLiteral(el1.toSet == el2.toSet) - case (FiniteLambda(m1, d1, _), FiniteLambda(m2, d2, _)) => BooleanLiteral(m1.toSet == m2.toSet && d1 == d2) - case _ => BooleanLiteral(lv == rv) - } - - case (CaseClass(cd, _), args) => - CaseClass(cd, args) - - case (AsInstanceOf(_, ct), Seq(ce)) => - if (isSubtypeOf(ce.getType, ct)) { - ce - } else { - throw RuntimeError("Cast error: cannot cast " + ce.asString + " to " + ct.asString) - } - - case (IsInstanceOf(_, ct), Seq(ce)) => - BooleanLiteral(isSubtypeOf(ce.getType, ct)) - - case (CaseClassSelector(ct1, _, sel), Seq(CaseClass(ct2, args))) if ct1 == ct2 => - args(ct1.classDef.selectorID2Index(sel)) - - case (Plus(_, _), Seq(InfiniteIntegerLiteral(i1), InfiniteIntegerLiteral(i2))) => - InfiniteIntegerLiteral(i1 + i2) - - case (Minus(_, _), Seq(InfiniteIntegerLiteral(i1), InfiniteIntegerLiteral(i2))) => - InfiniteIntegerLiteral(i1 - i2) - - case (Times(_, _), Seq(InfiniteIntegerLiteral(i1), InfiniteIntegerLiteral(i2))) => - InfiniteIntegerLiteral(i1 * i2) - - case (Division(_, _), Seq(InfiniteIntegerLiteral(i1), InfiniteIntegerLiteral(i2))) => - if (i2 != BigInt(0)) InfiniteIntegerLiteral(i1 / i2) - else throw RuntimeError("Division by 0.") - - case (Remainder(_, _), Seq(InfiniteIntegerLiteral(i1), InfiniteIntegerLiteral(i2))) => - if (i2 != BigInt(0)) InfiniteIntegerLiteral(i1 % i2) - else throw RuntimeError("Remainder of division by 0.") - - case (Modulo(_, _), Seq(InfiniteIntegerLiteral(i1), InfiniteIntegerLiteral(i2))) => - if (i2 < 0) - InfiniteIntegerLiteral(i1 mod (-i2)) - else if (i2 != BigInt(0)) - InfiniteIntegerLiteral(i1 mod i2) - else - throw RuntimeError("Modulo of division by 0.") - - case (UMinus(_), Seq(InfiniteIntegerLiteral(i))) => - InfiniteIntegerLiteral(-i) - - case (RealPlus(_, _), Seq(FractionalLiteral(ln, ld), FractionalLiteral(rn, rd))) => - normalizeFraction(FractionalLiteral(ln * rd + rn * ld, ld * rd)) - - case (RealMinus(_, _), Seq(FractionalLiteral(ln, ld), FractionalLiteral(rn, rd))) => - normalizeFraction(FractionalLiteral(ln * rd - rn * ld, ld * rd)) - - case (RealTimes(_, _), Seq(FractionalLiteral(ln, ld), FractionalLiteral(rn, rd))) => - normalizeFraction(FractionalLiteral(ln * rn, ld * rd)) - - case (RealDivision(_, _), Seq(FractionalLiteral(ln, ld), FractionalLiteral(rn, rd))) => - if (rn != 0) normalizeFraction(FractionalLiteral(ln * rd, ld * rn)) - else throw RuntimeError("Division by 0.") - - case (BVPlus(_, _), Seq(IntLiteral(i1), IntLiteral(i2))) => - IntLiteral(i1 + i2) - - case (BVMinus(_, _), Seq(IntLiteral(i1), IntLiteral(i2))) => - IntLiteral(i1 - i2) - - case (BVUMinus(_), Seq(IntLiteral(i))) => - IntLiteral(-i) - - case (RealUMinus(_), Seq(FractionalLiteral(n, d))) => - FractionalLiteral(-n, d) - - case (BVNot(_), Seq(IntLiteral(i))) => - IntLiteral(~i) - - case (BVTimes(_, _), Seq(IntLiteral(i1), IntLiteral(i2))) => - IntLiteral(i1 * i2) - - case (BVDivision(_, _), Seq(IntLiteral(i1), IntLiteral(i2))) => - if (i2 != 0) IntLiteral(i1 / i2) - else throw RuntimeError("Division by 0.") - - case (BVRemainder(_, _), Seq(IntLiteral(i1), IntLiteral(i2))) => - if (i2 != 0) IntLiteral(i1 % i2) - else throw RuntimeError("Remainder of division by 0.") - - case (BVAnd(_, _), Seq(IntLiteral(i1), IntLiteral(i2))) => - IntLiteral(i1 & i2) - - case (BVOr(_, _), Seq(IntLiteral(i1), IntLiteral(i2))) => - IntLiteral(i1 | i2) - - case (BVXOr(_, _), Seq(IntLiteral(i1), IntLiteral(i2))) => - IntLiteral(i1 ^ i2) - - case (BVShiftLeft(_, _), Seq(IntLiteral(i1), IntLiteral(i2))) => - IntLiteral(i1 << i2) - - case (BVAShiftRight(_, _), Seq(IntLiteral(i1), IntLiteral(i2))) => - IntLiteral(i1 >> i2) - - case (BVLShiftRight(_, _), Seq(IntLiteral(i1), IntLiteral(i2))) => - IntLiteral(i1 >>> i2) - - case (LessThan(_, _), Seq(el, er)) => - (el, er) match { - case (IntLiteral(i1), IntLiteral(i2)) => BooleanLiteral(i1 < i2) - case (InfiniteIntegerLiteral(i1), InfiniteIntegerLiteral(i2)) => BooleanLiteral(i1 < i2) - case (a@FractionalLiteral(_, _), b@FractionalLiteral(_, _)) => - val FractionalLiteral(n, _) = e(RealMinus(a, b)).head - BooleanLiteral(n < 0) - case (CharLiteral(c1), CharLiteral(c2)) => BooleanLiteral(c1 < c2) - case (le, re) => throw EvalError(typeErrorMsg(le, Int32Type)) - } - - case (GreaterThan(_, _), Seq(el, er)) => - (el, er) match { - case (IntLiteral(i1), IntLiteral(i2)) => BooleanLiteral(i1 > i2) - case (InfiniteIntegerLiteral(i1), InfiniteIntegerLiteral(i2)) => BooleanLiteral(i1 > i2) - case (a@FractionalLiteral(_, _), b@FractionalLiteral(_, _)) => - val FractionalLiteral(n, _) = e(RealMinus(a, b)).head - BooleanLiteral(n > 0) - case (CharLiteral(c1), CharLiteral(c2)) => BooleanLiteral(c1 > c2) - case (le, re) => throw EvalError(typeErrorMsg(le, Int32Type)) - } - - case (LessEquals(_, _), Seq(el, er)) => - (el, er) match { - case (IntLiteral(i1), IntLiteral(i2)) => BooleanLiteral(i1 <= i2) - case (InfiniteIntegerLiteral(i1), InfiniteIntegerLiteral(i2)) => BooleanLiteral(i1 <= i2) - case (a@FractionalLiteral(_, _), b@FractionalLiteral(_, _)) => - val FractionalLiteral(n, _) = e(RealMinus(a, b)).head - BooleanLiteral(n <= 0) - case (CharLiteral(c1), CharLiteral(c2)) => BooleanLiteral(c1 <= c2) - case (le, re) => throw EvalError(typeErrorMsg(le, Int32Type)) - } - - case (GreaterEquals(_, _), Seq(el, er)) => - (el, er) match { - case (IntLiteral(i1), IntLiteral(i2)) => BooleanLiteral(i1 >= i2) - case (InfiniteIntegerLiteral(i1), InfiniteIntegerLiteral(i2)) => BooleanLiteral(i1 >= i2) - case (a @ FractionalLiteral(_, _), b @ FractionalLiteral(_, _)) => - val FractionalLiteral(n, _) = e(RealMinus(a, b)).head - BooleanLiteral(n >= 0) - case (CharLiteral(c1), CharLiteral(c2)) => BooleanLiteral(c1 >= c2) - case (le, re) => throw EvalError(typeErrorMsg(le, Int32Type)) - } - - case (IsTyped(sa @ SetAdd(_, _), tpe), Seq( - IsTyped(FiniteSet(els1, _), SetType(tpe1)), - IsTyped(elem, tpe2) - )) => - FiniteSet( - els1 + elem, - leastUpperBound(tpe1, tpe2).getOrElse(throw EvalError(typeErrorMsg(sa, tpe))) - ) - - case (IsTyped(su @ SetUnion(s1, s2), tpe), Seq( - IsTyped(FiniteSet(els1, _), SetType(tpe1)), - IsTyped(FiniteSet(els2, _), SetType(tpe2)) - )) => - FiniteSet( - els1 ++ els2, - leastUpperBound(tpe1, tpe2).getOrElse(throw EvalError(typeErrorMsg(su, tpe))) - ) - - case (IsTyped(su @ SetIntersection(s1, s2), tpe), Seq( - IsTyped(FiniteSet(els1, _), SetType(tpe1)), - IsTyped(FiniteSet(els2, _), SetType(tpe2)) - )) => - FiniteSet( - els1 & els2, - leastUpperBound(tpe1, tpe2).getOrElse(throw EvalError(typeErrorMsg(su, tpe))) - ) - - case (IsTyped(su @ SetDifference(s1, s2), tpe), Seq( - IsTyped(FiniteSet(els1, _), SetType(tpe1)), - IsTyped(FiniteSet(els2, _), SetType(tpe2)) - )) => - FiniteSet( - els1 -- els2, - leastUpperBound(tpe1, tpe2).getOrElse(throw EvalError(typeErrorMsg(su, tpe))) - ) - - case (ElementOfSet(_, _), Seq(e, FiniteSet(els, _))) => - BooleanLiteral(els.contains(e)) - - case (SubsetOf(_, _), Seq(FiniteSet(els1, _), FiniteSet(els2, _))) => - BooleanLiteral(els1.subsetOf(els2)) - - case (SetCardinality(_), Seq(FiniteSet(els, _))) => - IntLiteral(els.size) - - case (FiniteSet(_, base), els) => - FiniteSet(els.toSet, base) - - case (IsTyped(ba @ BagAdd(_, _), tpe), Seq( - IsTyped(FiniteBag(els1, _), BagType(tpe1)), - IsTyped(e, tpe2) - )) => - FiniteBag( - els1 + (e -> (els1.getOrElse(e, InfiniteIntegerLiteral(0)) match { - case InfiniteIntegerLiteral(i) => InfiniteIntegerLiteral(i + 1) - case il => throw EvalError(typeErrorMsg(il, IntegerType)) - })), - leastUpperBound(tpe1, tpe2).getOrElse(throw EvalError(typeErrorMsg(ba, tpe))) - ) - - case (IsTyped(bu @ BagUnion(b1, b2), tpe), Seq( - IsTyped(FiniteBag(els1, _), BagType(tpe1)), - IsTyped(FiniteBag(els2, _), BagType(tpe2)) - )) => - FiniteBag( - (els1.keys ++ els2.keys).map(k => k -> { - ((els1.getOrElse(k, InfiniteIntegerLiteral(0)), els2.getOrElse(k, InfiniteIntegerLiteral(0))) match { - case (InfiniteIntegerLiteral(i1), InfiniteIntegerLiteral(i2)) => InfiniteIntegerLiteral(i1 + i2) - case (l, r) => throw EvalError(typeErrorMsg(l, IntegerType)) - }) - }).toMap, - leastUpperBound(tpe1, tpe2).getOrElse(throw EvalError(typeErrorMsg(bu, tpe))) - ) - - case (IsTyped(bi @ BagIntersection(s1, s2), tpe), Seq( - IsTyped(FiniteBag(els1, _), BagType(tpe1)), - IsTyped(FiniteBag(els2, _), BagType(tpe2)) - )) => - FiniteBag( - els1.flatMap { case (k, e) => - val res = (e, els2.getOrElse(k, InfiniteIntegerLiteral(0))) match { - case (InfiniteIntegerLiteral(i1), InfiniteIntegerLiteral(i2)) => i1 min i2 - case (l, r) => throw EvalError(typeErrorMsg(l, IntegerType)) - } - if (res <= 0) None else Some(k -> InfiniteIntegerLiteral(res)) - }, - leastUpperBound(tpe1, tpe2).getOrElse(throw EvalError(typeErrorMsg(bi, tpe))) - ) - - case (IsTyped(bd @ BagDifference(s1, s2), tpe), Seq( - IsTyped(FiniteBag(els1, _), BagType(tpe1)), - IsTyped(FiniteBag(els2, _), BagType(tpe2)) - )) => - FiniteBag( - els1.flatMap { case (k, e) => - val res = (e, els2.getOrElse(k, InfiniteIntegerLiteral(0))) match { - case (InfiniteIntegerLiteral(i1), InfiniteIntegerLiteral(i2)) => i1 - i2 - case (l, r) => throw EvalError(typeErrorMsg(l, IntegerType)) - } - if (res <= 0) None else Some(k -> InfiniteIntegerLiteral(res)) - }, - leastUpperBound(tpe1, tpe2).getOrElse(throw EvalError(typeErrorMsg(bd, tpe))) - ) - - case (MultiplicityInBag(_, _), Seq(e, FiniteBag(els, _))) => - els.getOrElse(e, InfiniteIntegerLiteral(0)) - - case (fb @ FiniteBag(_, _), els) => - val Operator(_, builder) = fb - builder(els) - - case (ArrayLength(_), Seq(FiniteArray(_, _, IntLiteral(length)))) => - IntLiteral(length) - - case (ArrayUpdated(_, _, _), Seq( - IsTyped(FiniteArray(elems, default, length), ArrayType(tp)), - IntLiteral(i), - v - )) => - finiteArray(elems.updated(i, v), default map {(_, length)}, tp) - - case (ArraySelect(_, _), Seq(fa@FiniteArray(elems, default, IntLiteral(length)), IntLiteral(index))) => - elems - .get(index) - .orElse(if (index >= 0 && index < length) default else None) - .getOrElse(throw RuntimeError(s"Array out of bounds error during evaluation:\n array = $fa, index = $index")) - - case (fa @ FiniteArray(_, _, _), subs) => - val Operator(_, builder) = fa - builder(subs) - - case (fm @ FiniteMap(_, _, _), subs) => - val Operator(_, builder) = fm - builder(subs) - - case (g @ MapApply(_, _), Seq(FiniteMap(m, _, _), k)) => - m.getOrElse(k, throw RuntimeError("Key not found: " + k.asString)) - - case (u@IsTyped(MapUnion(_, _), MapType(kT, vT)), Seq(FiniteMap(m1, _, _), FiniteMap(m2, _, _))) => - FiniteMap(m1 ++ m2, kT, vT) - - case (i@MapIsDefinedAt(_, _), Seq(FiniteMap(elems, _, _), k)) => - BooleanLiteral(elems.contains(k)) - - case (gv: GenericValue, Seq()) => - gv - - case (fl: FractionalLiteral, Seq()) => - normalizeFraction(fl) - - case (l: Literal[_], Seq()) => - l - - case (other, _) => - context.reporter.error(other.getPos, "Error: don't know how to handle " + other.asString + " in Evaluator ("+other.getClass+").") - throw EvalError("Unhandled case in Evaluator: " + other.asString) - - } - -} diff --git a/src/main/scala/inox/evaluators/TracingEvaluator.scala b/src/main/scala/inox/evaluators/TracingEvaluator.scala deleted file mode 100644 index dca816d87..000000000 --- a/src/main/scala/inox/evaluators/TracingEvaluator.scala +++ /dev/null @@ -1,118 +0,0 @@ -/* Copyright 2009-2016 EPFL, Lausanne */ - -package leon -package evaluators - -import purescala.Common._ -import purescala.Expressions._ -import purescala.Definitions._ -import purescala.Types._ - -class TracingEvaluator(ctx: LeonContext, prog: Program, maxSteps: Int = 1000) extends RecursiveEvaluator(ctx, prog, maxSteps) { - type RC = TracingRecContext - type GC = TracingGlobalContext - - def initRC(mappings: Map[Identifier, Expr]) = TracingRecContext(mappings, 2) - - def initGC(model: solvers.Model, check: Boolean) = new TracingGlobalContext(Nil, model, check) - - class TracingGlobalContext(var values: List[(Tree, Expr)], model: solvers.Model, check: Boolean) - extends GlobalContext(model, this.maxSteps, check) - - case class TracingRecContext(mappings: Map[Identifier, Expr], tracingFrames: Int) extends RecContext[TracingRecContext] { - def newVars(news: Map[Identifier, Expr]) = copy(mappings = news) - } - - override def e(expr: Expr)(implicit rctx: RC, gctx: GC): Expr = { - try { - val (res, recordedRes) = expr match { - case Let(i,ex,b) => - // We record the value of the val at the position of Let, not the value of the body. - val first = e(ex) - val res = e(b)(rctx.withNewVar(i, first), gctx) - (res, first) - - case p: Passes => - val r = e(p.asConstraint) - (r, r) - - case MatchExpr(scrut, cases) => - val rscrut = e(scrut) - - val r = cases.toStream.map(c => matchesCase(rscrut, c)).find(_.nonEmpty) match { - case Some(Some((c, mappings))) => - gctx.values ++= (Map(c.pattern -> rscrut) ++ mappings.map { case (id, v) => id.toVariable.setPos(id) -> v }) - - e(c.rhs)(rctx.withNewVars(mappings), gctx) - case _ => - throw RuntimeError("MatchError: "+rscrut+" did not match any of the cases") - } - - (r, r) - - case SpecializedFunctionInvocation(res) => (res, res) - - case fi @ FunctionInvocation(tfd, args) => - if (gctx.stepsLeft < 0) { - throw RuntimeError("Exceeded number of allocated methods calls ("+gctx.maxSteps+")") - } - gctx.stepsLeft -= 1 - - val evArgs = args.map(a => e(a)) - - // build a mapping for the function... - val frame = new TracingRecContext(tfd.paramSubst(evArgs), rctx.tracingFrames-1) - - if(tfd.hasPrecondition) { - e(tfd.precondition.get)(frame, gctx) match { - case BooleanLiteral(true) => - case BooleanLiteral(false) => - throw RuntimeError("Precondition violation for " + tfd.id.name + " reached in evaluation.: " + tfd.precondition.get) - case other => throw RuntimeError(typeErrorMsg(other, BooleanType)) - } - } - - if(!tfd.hasBody && !rctx.mappings.isDefinedAt(tfd.id)) { - throw EvalError("Tracing Evaluation of function with unknown implementation.") - } - - val body = tfd.body.getOrElse(rctx.mappings(tfd.id)) - val callResult = e(body)(frame, gctx) - - tfd.postcondition match { - case Some(post) => - - e(Application(post, Seq(callResult)))(frame, gctx) match { - case BooleanLiteral(true) => - case BooleanLiteral(false) => throw RuntimeError("Postcondition violation for " + tfd.id.name + " reached in evaluation.") - case other => throw EvalError(typeErrorMsg(other, BooleanType)) - } - case None => - } - - (callResult, callResult) - case _ => - val res = super.e(expr) - (res, res) - } - if (rctx.tracingFrames > 0) { - gctx.values ::= (expr -> recordedRes) - } - - res - } catch { - case ee @ EvalError(e) => - if (rctx.tracingFrames > 0) { - gctx.values ::= (expr -> Error(expr.getType, e)) - } - throw ee; - - case re @ RuntimeError(e) => - if (rctx.tracingFrames > 0) { - gctx.values ::= (expr -> Error(expr.getType, e)) - } - throw re; - } - } - -} diff --git a/src/main/scala/inox/evaluators/TrackingEvaluator.scala b/src/main/scala/inox/evaluators/TrackingEvaluator.scala deleted file mode 100644 index 7b652742f..000000000 --- a/src/main/scala/inox/evaluators/TrackingEvaluator.scala +++ /dev/null @@ -1,124 +0,0 @@ -/* Copyright 2009-2016 EPFL, Lausanne */ - -package leon -package evaluators - -import purescala.Common._ -import purescala.Definitions._ -import purescala.Expressions._ -import purescala.Types._ - -import scala.collection.mutable.{Map => MMap} - -/** An [[Evaluator]] that tracks information about the runtime call tree. - * Transitive dependencies between function calls are stored in [[fullCallGraph]]. - * Additionally, [[fiStatus]] tracks if each function invocation was successful or erroneous (led to an error). - */ -class TrackingEvaluator(ctx: LeonContext, prog: Program) extends RecursiveEvaluator(ctx, prog, 50000) with HasDefaultGlobalContext { - type RC = CollectingRecContext - - def initRC(mappings: Map[Identifier, Expr]) = CollectingRecContext(mappings, None) - - type FI = (FunDef, Seq[Expr]) - - // This is a call graph to track dependencies of function invocations. - // If fi1 calls fi2 but fails fi2's precondition, we consider it - // fi1's fault and we don't register the dependency. - private val callGraph : MMap[FI, Set[FI]] = MMap() - // Register a call without any edges towards other calls - private def registerNode(fi : FI) = if (!callGraph.contains(fi)) callGraph update (fi, Set()) - // Register an edge - also registers start and end nodes - private def registerCall(fi : FI, lastFI : Option[FI]) = { - lastFI foreach { lfi => - callGraph update (lfi, callGraph(lfi) + fi) - } - } - def fullCallGraph = leon.utils.GraphOps.transitiveClosure(callGraph.toMap) - - // Tracks if every function invocation succeeded or failed - private val fiStatus_ : MMap[FI, Boolean] = MMap().withDefaultValue(false) - private def registerSuccessful(fi : FI) = fiStatus_ update (fi, true ) - private def registerFailed (fi : FI) = fiStatus_ update (fi, false) - def fiStatus = fiStatus_.toMap.withDefaultValue(false) - - case class CollectingRecContext(mappings: Map[Identifier, Expr], lastFI : Option[FI]) extends RecContext[CollectingRecContext] { - def newVars(news: Map[Identifier, Expr]) = copy(news, lastFI) - def withLastFI(fi : FI) = copy(lastFI = Some(fi)) - } - - override def e(expr: Expr)(implicit rctx: RC, gctx: GC): Expr = expr match { - case SpecializedFunctionInvocation(res) => res - - case FunctionInvocation(tfd, args) => - if (gctx.stepsLeft < 0) { - throw RuntimeError("Exceeded number of allocated methods calls ("+gctx.maxSteps+")") - } - gctx.stepsLeft -= 1 - - val evArgs = args.map(a => e(a)) - - // build a mapping for the function... - val frameBlamingCaller = rctx.newVars(tfd.paramSubst(evArgs)) - - if(tfd.hasPrecondition) { - e(tfd.precondition.get)(frameBlamingCaller, gctx) match { - case BooleanLiteral(true) => - // We consider this function invocation successful, unless the opposite is proven. - registerSuccessful(tfd.fd, evArgs) - // Only register a call dependency if the call we depend on does not fail precondition - registerCall((tfd.fd, evArgs), rctx.lastFI) - registerNode((tfd.fd, evArgs)) - case BooleanLiteral(false) => - // Caller's fault! - rctx.lastFI foreach registerFailed - throw RuntimeError("Precondition violation for " + tfd.id.name + " reached in evaluation.: " + tfd.precondition.get) - case other => - // Caller's fault! - rctx.lastFI foreach registerFailed - throw RuntimeError(typeErrorMsg(other, BooleanType)) - } - } else { - registerSuccessful(tfd.fd, evArgs) - registerNode((tfd.fd, evArgs)) - registerCall((tfd.fd, evArgs), rctx.lastFI) - } - - if(!tfd.hasBody && !rctx.mappings.isDefinedAt(tfd.id)) { - throw EvalError("Tracking Evaluation of function with unknown implementation.") - } - - val body = tfd.body.getOrElse(rctx.mappings(tfd.id)) - - val frameBlamingCallee = frameBlamingCaller.withLastFI(tfd.fd, evArgs) - - val callResult = e(body)(frameBlamingCallee, gctx) - - tfd.postcondition match { - case Some(post) => - e(Application(post, Seq(callResult)))(frameBlamingCallee, gctx) match { - case BooleanLiteral(true) => - case BooleanLiteral(false) => - // Callee's fault - registerFailed(tfd.fd, evArgs) - throw RuntimeError("Postcondition violation for " + tfd.id.name + " reached in evaluation.") - case other => - // Callee's fault - registerFailed(tfd.fd, evArgs) - throw EvalError(typeErrorMsg(other, BooleanType)) - } - case None => - } - - callResult - - case other => - try { - super.e(other) - } catch { - case t : Throwable => - rctx.lastFI foreach registerFailed - throw t - } - } - -} diff --git a/src/main/scala/inox/solvers/EvaluatingSolver.scala b/src/main/scala/inox/solvers/EvaluatingSolver.scala deleted file mode 100644 index 292ef1605..000000000 --- a/src/main/scala/inox/solvers/EvaluatingSolver.scala +++ /dev/null @@ -1,20 +0,0 @@ -/* Copyright 2009-2016 EPFL, Lausanne */ - -package leon -package solvers - -import purescala.Definitions._ -import evaluators._ - -trait EvaluatingSolver extends Solver { - val program: Program - - val useCodeGen: Boolean - - val evaluator: DeterministicEvaluator = if (useCodeGen) { - new CodeGenEvaluator(context, program, sctx.bank) - } else { - new DefaultEvaluator(context, program, sctx.bank) - } -} - diff --git a/src/main/scala/inox/solvers/RawArray.scala b/src/main/scala/inox/solvers/RawArray.scala deleted file mode 100644 index e651bfea4..000000000 --- a/src/main/scala/inox/solvers/RawArray.scala +++ /dev/null @@ -1,81 +0,0 @@ -/* Copyright 2009-2016 EPFL, Lausanne */ - -package leon -package solvers - -import purescala.Types._ -import purescala.Expressions._ -import purescala.Extractors._ -import purescala.TypeOps._ -import purescala.{PrettyPrintable, PrinterContext} -import purescala.PrinterHelpers._ - -// Corresponds to a complete map (SMT Array), not a Leon/Scala array -// Only used within solvers or SMT for encoding purposes -case class RawArrayType(from: TypeTree, to: TypeTree) extends TypeTree with PrettyPrintable { - override def asString(implicit ctx: LeonContext) = { - s"RawArrayType[${from.asString}, ${to.asString}]" - } - - override def printWith(implicit pctx: PrinterContext) = { - p"RawArrayType[$from, $to]" - } -} - -// Corresponds to a raw array value, which is coerced to a Leon expr depending on target type (set/array) -case class RawArrayValue(keyTpe: TypeTree, elems: Map[Expr, Expr], default: Expr) extends Expr with Extractable { - - override def extract: Option[(Seq[Expr], (Seq[Expr]) => Expr)] = { - val linearized: Seq[Expr] = elems.toVector.flatMap(p => Seq(p._1, p._2)) :+ default - Some((linearized, es => { - val freshElems = es.dropRight(1).grouped(2).map(p => p(0) -> p(1)).toMap - RawArrayValue(keyTpe, freshElems, es.last) - })) - } - - val getType = RawArrayType(keyTpe, default.getType) - - override def asString(implicit ctx: LeonContext) = { - val elemsString = elems.map { case (k, v) => k.asString+" -> "+v.asString } mkString(", ") - s"RawArray[${keyTpe.asString}]($elemsString, default = ${default.asString})" - } -} - -case class RawArraySelect(array: Expr, index: Expr) extends Expr with Extractable with PrettyPrintable { - - override def extract: Option[(Seq[Expr], (Seq[Expr]) => Expr)] = - Some((Seq(array, index), es => RawArraySelect(es(0), es(1)))) - - val getType = array.getType match { - case RawArrayType(from, to) if isSubtypeOf(index.getType, from) => - to - case _ => - Untyped - } - - override def asString(implicit ctx: LeonContext) = { - s"$array($index)" - } - - override def printWith(implicit pctx: PrinterContext) = { - p"$array($index)" - } - -} - -case class RawArrayUpdated(array: Expr, index: Expr, newValue: Expr) extends Expr with Extractable with PrettyPrintable { - - val getType = array.getType - - override def extract: Option[(Seq[Expr], (Seq[Expr]) => Expr)] = - Some((Seq(array, index, newValue), es => RawArrayUpdated(es(0), es(1), es(2)))) - - override def asString(implicit ctx: LeonContext) = { - s"$array($index) = $newValue" - } - - override def printWith(implicit pctx: PrinterContext) = { - p"$array($index) = $newValue" - } - -} diff --git a/src/main/scala/inox/solvers/string/StringSolver.scala b/src/main/scala/inox/solvers/string/StringSolver.scala index 5fac95318..c39dc9043 100644 --- a/src/main/scala/inox/solvers/string/StringSolver.scala +++ b/src/main/scala/inox/solvers/string/StringSolver.scala @@ -11,16 +11,16 @@ import scala.annotation.tailrec */ object StringSolver { type Assignment = Map[Identifier, String] - + type StringFormToken = Either[String, Identifier] - + type StringForm = List[StringFormToken] - + type Equation = (StringForm, String) - + /** Sequences of equalities such as xyz"1"uv"2" = "1, 2" */ type Problem = List[Equation] - + def renderStringForm(sf: StringForm): String = sf match { case Left(const)::Nil => "\""+const+"\"" case Right(id)::Nil => id.toString @@ -28,7 +28,7 @@ object StringSolver { case Right(id)::q => id.toString + "+" + renderStringForm(q) case Nil => "" } - + def renderProblem(p: Problem): String = { def renderEquation(e: Equation): String = { renderStringForm(e._1) + "==\""+e._2+"\"" @@ -36,14 +36,14 @@ object StringSolver { p match {case Nil => "" case e::q => renderEquation(e) + ", " + renderProblem(q)} } - + /** Evaluates a String form. Requires the solution to have an assignment to all identifiers. */ @tailrec def evaluate(s: Assignment, acc: StringBuffer = new StringBuffer(""))(sf: StringForm): String = sf match { case Nil => acc.toString case Left(constant)::q => evaluate(s, acc append constant)(q) case Right(identifier)::q => evaluate(s, acc append s(identifier))(q) } - + /** Assigns the new values to the equations and simplify them at the same time. */ @tailrec def reduceStringForm(s: Assignment, acc: ListBuffer[StringFormToken] = ListBuffer())(sf: StringForm): StringForm = sf match { case Nil => acc.toList @@ -702,4 +702,4 @@ object StringSolver { solution <- minimizeChanges(newSolutions_nonredundant, newProblem, ifVariable, initialMapping: Assignment) } yield solution } -} \ No newline at end of file +} diff --git a/src/main/scala/inox/trees/Quantification.scala b/src/main/scala/inox/solvers/unrolling/Quantification.scala similarity index 100% rename from src/main/scala/inox/trees/Quantification.scala rename to src/main/scala/inox/solvers/unrolling/Quantification.scala diff --git a/src/main/scala/inox/trees/CallGraph.scala b/src/main/scala/inox/trees/CallGraph.scala index 63fe2c570..0c5d1b2b0 100644 --- a/src/main/scala/inox/trees/CallGraph.scala +++ b/src/main/scala/inox/trees/CallGraph.scala @@ -1,7 +1,7 @@ /* Copyright 2009-2016 EPFL, Lausanne */ -package leon -package purescala +package inox +package trees import Definitions._ import Expressions._ diff --git a/src/main/scala/inox/trees/Constructors.scala b/src/main/scala/inox/trees/Constructors.scala index 22844a422..85ec99a73 100644 --- a/src/main/scala/inox/trees/Constructors.scala +++ b/src/main/scala/inox/trees/Constructors.scala @@ -1,15 +1,7 @@ /* Copyright 2009-2016 EPFL, Lausanne */ -package leon -package purescala - -import Expressions._ -import Extractors._ -import ExprOps._ -import Definitions._ -import TypeOps._ -import Common._ -import Types._ +package inox +package trees /** Provides constructors for [[purescala.Expressions]]. * @@ -17,7 +9,8 @@ import Types._ * potentially use a different expression node if one is more suited. * @define encodingof Encoding of * */ -trait Constructors { self: Expressions => +trait Constructors { self: ExprOps => + import trees._ /** If `isTuple`: * `tupleSelect(tupleWrap(Seq(Tuple(x,y))),1) -> x` @@ -45,7 +38,7 @@ trait Constructors { self: Expressions => * @see [[purescala.Expressions.Let]] */ def let(id: Identifier, e: Expr, bd: Expr) = { - if (variablesOf(bd) contains id) + if (exprOps.variablesOf(bd) contains id) Let(id, e, bd) else bd } diff --git a/src/main/scala/inox/trees/DefinitionTransformer.scala b/src/main/scala/inox/trees/DefinitionTransformer.scala deleted file mode 100644 index f27a11046..000000000 --- a/src/main/scala/inox/trees/DefinitionTransformer.scala +++ /dev/null @@ -1,218 +0,0 @@ -/* Copyright 2009-2015 EPFL, Lausanne */ - -package leon -package purescala - -import Common._ -import Definitions._ -import Expressions._ -import Types._ - -import utils._ -import scala.collection.mutable.{Map => MutableMap, Set => MutableSet} - -class DefinitionTransformer( - idMap: Bijection[Identifier, Identifier] = new Bijection[Identifier, Identifier], - fdMap: Bijection[FunDef , FunDef ] = new Bijection[FunDef , FunDef ], - cdMap: Bijection[ClassDef , ClassDef ] = new Bijection[ClassDef , ClassDef ]) extends TreeTransformer { - - private def transformId(id: Identifier, freshen: Boolean): Identifier = { - val ntpe = transform(id.getType) - if (ntpe == id.getType && !freshen) id else id.duplicate(tpe = ntpe) - } - - def transformType(tpe: TypeTree): Option[TypeTree] = None - final override def transform(tpe: TypeTree): TypeTree = { - super.transform(transformType(tpe).getOrElse(tpe)) - } - - final override def transform(id: Identifier): Identifier = { - val ntpe = transform(id.getType) - idMap.getB(id) match { - case Some(nid) if ntpe == nid.getType => nid - case _ => - val nid = transformId(id, false) - idMap += id -> nid - nid - } - } - - def transformExpr(e: Expr)(implicit bindings: Map[Identifier, Identifier]): Option[Expr] = None - final override def transform(e: Expr)(implicit bindings: Map[Identifier, Identifier]): Expr = { - transformExpr(e) match { - case Some(r) => super.transform(r) - case None => super.transform(e) - } - } - - protected def transformFunDef(fd: FunDef): Option[FunDef] = None - final override def transform(fd: FunDef): FunDef = { - if ((fdMap containsB fd) || (tmpFdMap containsB fd)) fd - else if (tmpFdMap containsA fd) tmpFdMap.toB(fd) - else fdMap.getBorElse(fd, { - transformDefs(fd) - fdMap.toB(fd) - }) - } - - - /** Override this to provide specific ClassDef transform - * - * It is guaranteed to only be called once per ClassDef accross the whole - * program transformation, and won't be applied recursively - * to the transformed class. - * - * This design choice is to allow for a typical use case in which you - * want to add a field to every single case classes in your program, - * and for obvious reasons you don't want to apply this transformation - * recursively on transformed case classes. Another typical case is - * to duplicate the whole program, which needs duplicating each case - * class by freshening all the fields. Without this specific contract, - * client implementation of such functionalities would require the use - * of a local map to track which classes are in the program before starting - * the transformation, and which classes was already transformed. - */ - protected def transformClassDef(cd: ClassDef): Option[ClassDef] = None - final override def transform(cd: ClassDef): ClassDef = { - if ((cdMap containsB cd) || (tmpCdMap containsB cd)) cd - else if (tmpCdMap containsA cd) tmpCdMap.toB(cd) - else - cdMap.getBorElse(cd, { - transformDefs(cd) - cdMap.toB(cd) - }) - } - - private val dependencies = new DependencyFinder - private val tmpCdMap = new Bijection[ClassDef, ClassDef] - private val tmpFdMap = new Bijection[FunDef , FunDef ] - private val fieldsSet: MutableSet[CaseClassDef] = MutableSet.empty[CaseClassDef] - - private def transformDefs(base: Definition): Unit = { - val (cds, fds) = { - val deps = dependencies(base) + base - val (c, f) = deps.partition(_.isInstanceOf[ClassDef]) - val cds = c.map(_.asInstanceOf[ClassDef]).filter(cd => !(cdMap containsA cd) && !(cdMap containsB cd)) - val fds = f.map(_.asInstanceOf[FunDef]).filter(fd => !(fdMap containsA fd) && !(fdMap containsB fd)) - (cds, fds) - } - - val transformedCds = (for (cd <- cds if !(cdMap containsA cd) && !(cdMap containsB cd)) yield { - val ncd = transformClassDef(cd).getOrElse(cd) - tmpCdMap += cd -> ncd - if (cd ne ncd) Some(cd -> ncd) else None - }).flatten.toMap - - val transformedFds = (for (fd <- fds if !(fdMap containsA fd) && !(fdMap containsB fd)) yield { - val nfd = transformFunDef(fd).getOrElse(fd) - tmpFdMap += fd -> nfd - if (fd ne nfd) Some(fd -> nfd) else None - }).flatten.toMap - - val req: Set[Definition] = { - val requireCache: MutableMap[Definition, Boolean] = MutableMap.empty - def required(d: Definition): Boolean = requireCache.getOrElse(d, { - val res = d match { - case funDef: FunDef => - val (fd, checkBody) = transformedFds.get(funDef) match { - case Some(fd) => (fd, false) - case None => (funDef, true) - } - - val newReturn = transform(fd.returnType) - lazy val newParams = fd.params.map(vd => ValDef(transform(vd.id))) - newReturn != fd.returnType || newParams != fd.params || (checkBody && { - val newBody = transform(fd.fullBody)((fd.params.map(_.id) zip newParams.map(_.id)).toMap) - newBody != fd.fullBody - }) - - case cd: ClassDef => - !(transformedCds contains cd) && - (cd.fieldsIds.exists(id => transform(id.getType) != id.getType) || - cd.invariant.exists(required)) - - case _ => scala.sys.error("Should never happen!?") - } - - requireCache += d -> res - res - }) - - val filtered = (cds ++ fds) filter required - - // clear can only take place after all calls to required(_) have taken place - tmpCdMap.clear() - tmpFdMap.clear() - - filtered - } - - val (fdsToDup: Set[FunDef], cdsToDup: Set[ClassDef]) = { - val prevTransformed = cdMap.aSet.filter(a => a ne cdMap.toB(a)) ++ fdMap.aSet.filter(a => a ne fdMap.toB(a)) - val reqs = req ++ transformedCds.keys ++ transformedFds.keys ++ prevTransformed - - val cdsToDup = cds filter { cd => req(cd) || - (!(transformedCds contains cd) && (dependencies(cd) & reqs).nonEmpty) || - ((transformedCds contains cd) && ((cd.root +: cd.root.knownDescendants).toSet & req).nonEmpty) } - - val fdsToDup = fds filter (fd => req(fd) || { - val deps = dependencies(fd) - (deps exists { case cd: ClassDef => cdsToDup(cd) case _ => false }) || - (!(transformedFds contains fd) && (deps & reqs).nonEmpty) }) - - (fdsToDup, cdsToDup) - } - - val fdsToTransform = (transformedFds.keys filterNot fdsToDup).toSet - val cdsToTransform = (transformedCds.keys filterNot cdsToDup).toSet - - val fdsToKeep = fds filterNot (fd => fdsToDup(fd) || fdsToTransform(fd)) - val cdsToKeep = cds filterNot (cd => cdsToDup(cd) || cdsToTransform(cd)) - - fdMap ++= fdsToKeep.map(fd => fd -> fd) ++ fdsToTransform.map(fd => fd -> transformedFds(fd)) - cdMap ++= cdsToKeep.map(cd => cd -> cd) ++ cdsToTransform.map(cd => cd -> transformedCds(cd)) - - def duplicateCd(cd: ClassDef): ClassDef = cdMap.cachedB(cd) { - val parent = cd.parent.map(act => act.copy(classDef = duplicateCd(act.classDef).asInstanceOf[AbstractClassDef])) - transformedCds.getOrElse(cd, cd) match { - case acd: AbstractClassDef => acd.duplicate(id = transformId(acd.id, true), parent = parent) - case ccd: CaseClassDef => ccd.duplicate(id = transformId(ccd.id, true), parent = parent) - } - } - - def duplicateFd(fd: FunDef): FunDef = fdMap.cachedB(fd) { - val ffd = transformedFds.getOrElse(fd, fd) - val newId = transformId(ffd.id, true) - val newReturn = transform(ffd.returnType) - val newParams = ffd.params map (vd => ValDef(transformId(vd.id, true))) - ffd.duplicate(id = newId, params = newParams, returnType = newReturn) - } - - for (cd <- cdsToDup) duplicateCd(cd) - for (fd <- fdsToDup) duplicateFd(fd) - - for (cd <- cdsToDup ++ cdsToTransform) { - val newCd = cdMap.toB(cd) - cd.invariant.foreach(fd => newCd.setInvariant(transform(fd))) - newCd match { - case (newCcd: CaseClassDef) => - if(!fieldsSet(newCcd)) { - //newCcd is duplicated from the original ccd, so it will always have defined fields, - //and they will the same as the original ccd fields. - newCcd.setFields(newCcd.fields.map(vd => ValDef(transformId(vd.id, true)))) - fieldsSet += newCcd - } - case _ => - } - } - - for (fd <- fdsToDup ++ fdsToTransform) { - val nfd = fdMap.toB(fd) - val bindings = (fd.params zip nfd.params).map(p => p._1.id -> p._2.id).toMap ++ - nfd.params.map(vd => vd.id -> vd.id) - - nfd.fullBody = transform(nfd.fullBody)(bindings) - } - } -} - diff --git a/src/main/scala/inox/trees/Definitions.scala b/src/main/scala/inox/trees/Definitions.scala index ded2b3615..7d4c8efd6 100644 --- a/src/main/scala/inox/trees/Definitions.scala +++ b/src/main/scala/inox/trees/Definitions.scala @@ -3,16 +3,9 @@ package leon package purescala -import utils.Library -import Common._ -import Expressions._ -import ExprOps._ -import Types._ -import TypeOps._ - import scala.collection.mutable.{Map => MutableMap} -object Definitions { +trait Definitions { self: Trees => sealed trait Definition extends Tree { val id: Identifier @@ -88,10 +81,10 @@ object Definitions { // Compiler annotations given in the source code as @annot case class Annotation(annot: String, args: Seq[Option[Any]]) extends FunctionFlag with ClassFlag + // Class has ADT invariant method + case class HasADTInvariant(id: Identifier) extends ClassFlag // Is inlined case object IsInlined extends FunctionFlag - // Is an ADT invariant method - case object IsADTInvariant(id: Identifier) extends FunctionFlag /** Represents a class definition (either an abstract- or a case-class) */ sealed trait ClassDef extends Definition { @@ -105,11 +98,8 @@ object Definitions { def hasParent = parent.isDefined - def invariana(implicit p: Program)t: Option[FunDef] = { - // TODO - parent.flatMap(_.classDef.invariant).orElse(_invariant) - - } + def invariant(implicit p: Program): Option[FunDef] = + flags.collect { case HasADTInvariant(id) => id }.map(p.getFunction) def annotations: Set[String] = extAnnotations.keySet def extAnnotations: Map[String, Seq[Option[Any]]] = flags.collect { case Annotation(s, args) => s -> args }.toMap diff --git a/src/main/scala/inox/trees/DependencyFinder.scala b/src/main/scala/inox/trees/DependencyFinder.scala deleted file mode 100644 index 95e894f16..000000000 --- a/src/main/scala/inox/trees/DependencyFinder.scala +++ /dev/null @@ -1,103 +0,0 @@ -/* Copyright 2009-2015 EPFL, Lausanne */ - -package leon -package purescala - -import Common._ -import Definitions._ -import Expressions._ -import Extractors._ -import Types._ - -import scala.collection.mutable.{Map => MutableMap, Set => MutableSet} - -/** Accumulate dependency information for a given program */ -class DependencyFinder(p: Program) { - private val deps: MutableMap[Identifier, Set[Identifier]] = MutableMap.empty - - /** Return all dependencies for a given Definition - * - * Use a cache internally, so two calls (from the same instance of DependencyFinder) - * will return the same Set without re-computing. A dependency for ClassDef `d` is - * any ClassDef references by the fields of any class in the hierarch of the `d`, as - * well as any class invariants in the hierarchy. - * - * Dependencies for FunDef are any ClassDef that appear in any types (parameter list, - * return value) as well as transitive dependencies via the body (a function invocation - * in the body will generate a dependency to the fundef of the call). - * - * A def does not depend on itself by default. But if it calls itself recursively, or - * via some transitive dependencies, then the return set will contain the input def. - */ - def apply(d: Definition): Set[Definition] = deps.getOrElseUpdate(d, { - new Finder(d).dependencies - }) - - private class Finder(private var current: Definition) extends TreeTraverser { - val foundDeps: MutableMap[Definition, MutableSet[Definition]] = MutableMap.empty - - private def withCurrent[T](d: Definition)(b: => T): T = { - if (!(foundDeps contains d)) foundDeps(d) = MutableSet.empty - val c = current - current = d - val res = b - current = c - res - } - - override def traverse(id: Identifier): Unit = traverse(id.getType) - - override def traverse(cd: ClassDef): Unit = if (!foundDeps(current)(cd)) { - foundDeps(current) += cd - if (!(deps contains cd) && !(foundDeps contains cd)) traverseClassDef(cd) - } - - private def traverseClassDef(cd: ClassDef): Unit = { - for (cd <- cd.root.knownDescendants :+ cd) { - cd.invariant foreach (fd => withCurrent(cd)(traverse(fd))) - withCurrent(cd)(cd.fieldsIds foreach traverse) - cd.parent foreach { p => - foundDeps(p.classDef) = foundDeps.getOrElse(p.classDef, MutableSet.empty) + cd - foundDeps(cd) = foundDeps.getOrElse(cd, MutableSet.empty) + p.classDef - } - } - } - - override def traverse(fd: FunDef): Unit = if (!foundDeps(current)(fd)) { - foundDeps(current) += fd - if (!(deps contains fd) && !(foundDeps contains fd)) traverseFunDef(fd) - } - - private def traverseFunDef(fd: FunDef): Unit = withCurrent(fd) { - fd.params foreach (vd => traverse(vd.id)) - traverse(fd.returnType) - traverse(fd.fullBody) - } - - def dependencies: Set[Definition] = { - current match { - case fd: FunDef => traverseFunDef(fd) - case cd: ClassDef => traverseClassDef(cd) - case _ => - } - - for ((d, ds) <- foundDeps) { - deps(d) = deps.getOrElse(d, Set.empty) ++ ds - } - - var changed = false - do { - changed = false - for ((d, ds) <- deps.toSeq) { - val next = ds ++ ds.flatMap(d => deps.getOrElse(d, Set.empty)) - if (!(next subsetOf ds)) { - deps(d) = next - changed = true - } - } - } while (changed) - - deps(current) - } - } -} diff --git a/src/main/scala/inox/trees/ExprOps.scala b/src/main/scala/inox/trees/ExprOps.scala index e3406cbbf..50c9de76f 100644 --- a/src/main/scala/inox/trees/ExprOps.scala +++ b/src/main/scala/inox/trees/ExprOps.scala @@ -3,16 +3,6 @@ package leon package purescala -import Common._ -import Types._ -import Definitions._ -import Expressions._ -import Extractors._ -import Constructors._ -import utils._ -import solvers._ -import scala.language.implicitConversions - /** Provides functions to manipulate [[purescala.Expressions]]. * * This object provides a few generic operations on Leon expressions, @@ -32,7 +22,9 @@ import scala.language.implicitConversions * operations on Leon expressions. * */ -trait ExprOps extends GenTreeOps[Expr] with Extractors { self: Trees => +trait ExprOps extends GenTreeOps[Expr] with Constructors with Paths { + val trees: Trees + import trees._ val Deconstructor = Operator diff --git a/src/main/scala/inox/trees/Extractors.scala b/src/main/scala/inox/trees/Extractors.scala index ac3a22663..19998d88b 100644 --- a/src/main/scala/inox/trees/Extractors.scala +++ b/src/main/scala/inox/trees/Extractors.scala @@ -1,12 +1,7 @@ /* Copyright 2009-2016 EPFL, Lausanne */ -package leon -package purescala - -import Expressions._ -import Common._ -import Types._ -import Constructors._ +package inox +package trees trait Extractors { self: Expressions => @@ -23,7 +18,10 @@ trait Extractors { self: Expressions => * one need to simplify the tree, it is easy to write/call a simplification * function that would simply apply the corresponding constructor for each node. */ - object Operator extends TreeExtractor[Expr] { + object Operator extends TreeExtractor { + val trees: Extractors.this.trees = Extractors.this.trees + type SubTree = trees.Expr + def unapply(expr: Expr): Option[(Seq[Expr], (Seq[Expr]) => Expr)] = expr match { /* Unary operators */ case Not(t) => diff --git a/src/main/scala/inox/trees/GenTreeOps.scala b/src/main/scala/inox/trees/GenTreeOps.scala index 286b1ae23..35cde2198 100644 --- a/src/main/scala/inox/trees/GenTreeOps.scala +++ b/src/main/scala/inox/trees/GenTreeOps.scala @@ -1,17 +1,18 @@ /* Copyright 2009-2015 EPFL, Lausanne */ -package leon -package purescala - -import Common._ -import utils._ +package inox +package trees /** A type that pattern matches agains a type of [[Tree]] and extracts it subtrees, * and a builder that reconstructs a tree of the same type from subtrees. * * @tparam SubTree The type of the tree */ -trait TreeExtractor[SubTree <: Tree] { +trait TreeExtractor { + val trees: Trees + import trees._ + + type SubTree <: Tree def unapply(e: SubTree): Option[(Seq[SubTree], (Seq[SubTree]) => SubTree)] } @@ -19,10 +20,17 @@ trait TreeExtractor[SubTree <: Tree] { * * @tparam SubTree The type of the tree */ -trait GenTreeOps[SubTree <: Tree] { +trait GenTreeOps { + val trees: Trees + import trees._ + + type SubTree <: Tree /** An extractor for [[SubTree]]*/ - val Deconstructor: TreeExtractor[SubTree] + val Deconstructor: TreeExtractor { + val trees: GenTreeOps.this.trees + type SubTree <: GenTreeOps.this.SubTree + } /* ======== * Core API diff --git a/src/main/scala/inox/trees/Path.scala b/src/main/scala/inox/trees/Path.scala deleted file mode 100644 index d6e85d938..000000000 --- a/src/main/scala/inox/trees/Path.scala +++ /dev/null @@ -1,231 +0,0 @@ -/* Copyright 2009-2016 EPFL, Lausanne */ - -package leon -package purescala - -import Common._ -import Definitions._ -import Expressions._ -import Constructors._ -import ExprOps._ -import Types._ - -object Path { - final type Element = Either[(Identifier, Expr), Expr] - def empty: Path = new Path(Seq.empty) - def apply(p: Expr): Path = p match { - case Let(i, e, b) => new Path(Seq(Left(i -> e))) merge apply(b) - case _ => new Path(Seq(Right(p))) - } - def apply(path: Seq[Expr]): Path = new Path(path.map(Right(_))) -} - -/** Encodes path conditions - * - * Paths are encoded as an (ordered) series of let-bindings and boolean - * propositions. A path is satisfiable iff all propositions are true - * in the context of the provided let-bindings. - * - * This encoding enables let-bindings over types for which equality is - * not defined, whereas an encoding of let-bindings with equalities - * could introduce non-sensical equations. - */ -class Path private[purescala]( - private[purescala] val elements: Seq[Path.Element]) extends Printable { - - import Path.Element - - /** Add a binding to this [[Path]] */ - def withBinding(p: (Identifier, Expr)) = { - def exprOf(e: Element) = e match { case Right(e) => e; case Left((_, e)) => e } - val (before, after) = elements span (el => !variablesOf(exprOf(el)).contains(p._1)) - new Path(before ++ Seq(Left(p)) ++ after) - } - def withBindings(ps: Iterable[(Identifier, Expr)]) = { - ps.foldLeft(this)( _ withBinding _ ) - } - - /** Add a condition to this [[Path]] */ - def withCond(e: Expr) = { - if (e == BooleanLiteral(true)) this - else new Path(elements :+ Right(e)) - } - def withConds(es: Iterable[Expr]) = new Path(elements ++ es.filterNot( _ == BooleanLiteral(true)).map(Right(_))) - - /** Remove bound variables from this [[Path]] - * @param ids the bound variables to remove - */ - def --(ids: Set[Identifier]) = new Path(elements.filterNot(_.left.exists(p => ids(p._1)))) - - /** Appends `that` path at the end of `this` */ - def merge(that: Path): Path = new Path(elements ++ that.elements) - - /** Appends `those` paths at the end of `this` */ - def merge(those: Traversable[Path]): Path = those.foldLeft(this)(_ merge _) - - /** Transforms all expressions inside the path - * - * Expressions in a path appear both on the right-hand side of let binders - * and in boolean path conditions. - */ - def map(f: Expr => Expr) = new Path(elements.map(_.left.map { case (id, e) => id -> f(e) }.right.map(f))) - - /** Splits the path on predicate `p` - * - * The path is split into - * 1. the sub-path containing all conditions that pass `p` (and all let-bindings), and - * 2. the sequence of conditions that didn't pass `p`. - */ - def partition(p: Expr => Boolean): (Path, Seq[Expr]) = { - val (passed, failed) = elements.partition { - case Right(e) => p(e) - case Left(_) => true - } - - (new Path(passed), failed.flatMap(_.right.toOption)) - } - - /** Check if the path is empty - * - * A path is empty iff it contains no let-bindings and its path condition is trivial. - */ - def isEmpty = elements.forall { - case Right(BooleanLiteral(true)) => true - case _ => false - } - - /** Returns the negation of a path - * - * Path negation requires folding the path into a proposition and negating it. - * However, all external let-binders can be preserved in the resulting path, thus - * avoiding let-binding dupplication in future path foldings. - */ - def negate: Path = { - val (outers, rest) = elements.span(_.isLeft) - new Path(outers :+ Right(not(fold[Expr](BooleanLiteral(true), let, Constructors.and(_, _))(rest)))) - } - - /** Returns a new path which depends ONLY on provided ids. - * - * Let-bindings obviously depend on some `id` if it corresponds to the bound - * identifier. An expression depends on an `id` iff the identifier is - * contained within the expression. - * - * This method makes little sense on its own and will typically be used from - * within a fixpoint computation where the `ids` set is iteratively computed - * by performing [[filterByIds]] calls on some (unchaning) base [[Path]]. - * - * @see [[leon.purescala.FunctionClosure.close]] for an example usecase. - */ - def filterByIds(ids: Set[Identifier]): Path = { - def containsIds(ids: Set[Identifier])(e: Expr): Boolean = exists{ - case Variable(id) => ids.contains(id) - case _ => false - }(e) - - val newElements = elements.filter{ - case Left((id, e)) => ids.contains(id) || containsIds(ids)(e) - case Right(e) => containsIds(ids)(e) - } - new Path(newElements) - } - - /** Free variables within the path */ - lazy val variables: Set[Identifier] = fold[Set[Identifier]](Set.empty, - (id, e, res) => res - id ++ variablesOf(e), (e, res) => res ++ variablesOf(e) - )(elements) - - lazy val bindings: Seq[(Identifier, Expr)] = elements.collect { case Left(p) => p } - lazy val boundIds = bindings map (_._1) - lazy val conditions: Seq[Expr] = elements.collect { case Right(e) => e } - - def isBound(id: Identifier): Boolean = bindings.exists(p => p._1 == id) - - /** Fold the path elements - * - * This function takes two combiner functions, one for let bindings and one - * for proposition expressions. - */ - private def fold[T](base: T, combineLet: (Identifier, Expr, T) => T, combineCond: (Expr, T) => T) - (elems: Seq[Element]): T = elems.foldRight(base) { - case (Left((id, e)), res) => combineLet(id, e, res) - case (Right(e), res) => combineCond(e, res) - } - - /** Folds the path elements over a distributive proposition combinator [[combine]] - * - * Certain combiners can be distributive over let-binding folds. Namely, one - * requires that `combine(let a = b in e1, e2)` be equivalent to - * `let a = b in combine(e1, e2)` (where a \not\in FV(e2)). This is the case for - * - conjunction [[and]] - * - implication [[implies]] - */ - private def distributiveClause(base: Expr, combine: (Expr, Expr) => Expr): Expr = { - val (outers, rest) = elements.span(_.isLeft) - val inner = fold[Expr](base, let, combine)(rest) - fold[Expr](inner, let, (_,_) => scala.sys.error("Should never happen!"))(outers) - } - - /** Folds the path into a conjunct with the expression `base` */ - def and(base: Expr) = distributiveClause(base, Constructors.and(_, _)) - - /** Fold the path into an implication of `base`, namely `path ==> base` */ - def implies(base: Expr) = distributiveClause(base, Constructors.implies) - - /** Folds the path into a `require` wrapping the expression `body` - * - * The function takes additional optional parameters - * - [[pre]] if one wishes to mix a pre-existing precondition into the final - * [[leon.purescala.Expressions.Require]], and - * - [[post]] for mixing a postcondition ([[leon.purescala.Expressions.Ensuring]]) in. - */ - def specs(body: Expr, pre: Expr = BooleanLiteral(true), post: Expr = NoTree(BooleanType)) = { - val (outers, rest) = elements.span(_.isLeft) - val cond = fold[Expr](BooleanLiteral(true), let, Constructors.and(_, _))(rest) - - def wrap(e: Expr): Expr = { - val bindings = rest.collect { case Left((id, e)) => id -> e } - val idSubst = bindings.map(p => p._1 -> p._1.freshen).toMap - val substMap = idSubst.mapValues(_.toVariable) - val replace = replaceFromIDs(substMap, _: Expr) - bindings.foldRight(replace(e)) { case ((id, e), b) => let(idSubst(id), replace(e), b) } - } - - val req = Require(Constructors.and(cond, wrap(pre)), wrap(body)) - val full = post match { - case l @ Lambda(args, body) => Ensuring(req, Lambda(args, wrap(body)).copiedFrom(l)) - case _ => req - } - - fold[Expr](full, let, (_, _) => scala.sys.error("Should never happen!"))(outers) - } - - /** Folds the path into the associated boolean proposition */ - lazy val toClause: Expr = and(BooleanLiteral(true)) - - /** Like [[toClause]] but doesn't simplify final path through constructors - * from [[leon.purescala.Constructors]] */ - lazy val fullClause: Expr = fold[Expr](BooleanLiteral(true), Let, And(_, _))(elements) - - /** Folds the path into a boolean proposition where let-bindings are - * replaced by equations. - * - * CAUTION: Should only be used once INSIDE the solver!! - */ - lazy val toPath: Expr = andJoin(elements.map { - case Left((id, e)) => Equals(id.toVariable, e) - case Right(e) => e - }) - - override def equals(that: Any): Boolean = that match { - case p: Path => elements == p.elements - case _ => false - } - - override def hashCode: Int = elements.hashCode - - override def toString = asString(LeonContext.printNames) - def asString(implicit ctx: LeonContext): String = fullClause.asString - def asString(pgm: Program)(implicit ctx: LeonContext): String = fullClause.asString(pgm) -} - diff --git a/src/main/scala/inox/trees/Paths.scala b/src/main/scala/inox/trees/Paths.scala new file mode 100644 index 000000000..a5c0d7633 --- /dev/null +++ b/src/main/scala/inox/trees/Paths.scala @@ -0,0 +1,232 @@ +/* Copyright 2009-2016 EPFL, Lausanne */ + +package inox +package trees + +trait Paths { self: ExprOps => + import trees._ + + object Path { + final type Element = Either[(Identifier, Expr), Expr] + + def empty: Path = new Path(Seq.empty) + + def apply(p: Expr): Path = p match { + case Let(i, e, b) => new Path(Seq(Left(i -> e))) merge apply(b) + case _ => new Path(Seq(Right(p))) + } + + def apply(path: Seq[Expr]): Path = new Path(path.map(Right(_))) + } + + /** Encodes path conditions + * + * Paths are encoded as an (ordered) series of let-bindings and boolean + * propositions. A path is satisfiable iff all propositions are true + * in the context of the provided let-bindings. + * + * This encoding enables let-bindings over types for which equality is + * not defined, whereas an encoding of let-bindings with equalities + * could introduce non-sensical equations. + */ + class Path private[purescala]( + private[purescala] val elements: Seq[Path.Element]) extends Printable { + + import Path.Element + + /** Add a binding to this [[Path]] */ + def withBinding(p: (Identifier, Expr)) = { + def exprOf(e: Element) = e match { case Right(e) => e; case Left((_, e)) => e } + val (before, after) = elements span (el => !variablesOf(exprOf(el)).contains(p._1)) + new Path(before ++ Seq(Left(p)) ++ after) + } + + def withBindings(ps: Iterable[(Identifier, Expr)]) = { + ps.foldLeft(this)( _ withBinding _ ) + } + + /** Add a condition to this [[Path]] */ + def withCond(e: Expr) = { + if (e == BooleanLiteral(true)) this + else new Path(elements :+ Right(e)) + } + + def withConds(es: Iterable[Expr]) = new Path(elements ++ es.filterNot( _ == BooleanLiteral(true)).map(Right(_))) + + /** Remove bound variables from this [[Path]] + * @param ids the bound variables to remove + */ + def --(ids: Set[Identifier]) = new Path(elements.filterNot(_.left.exists(p => ids(p._1)))) + + /** Appends `that` path at the end of `this` */ + def merge(that: Path): Path = new Path(elements ++ that.elements) + + /** Appends `those` paths at the end of `this` */ + def merge(those: Traversable[Path]): Path = those.foldLeft(this)(_ merge _) + + /** Transforms all expressions inside the path + * + * Expressions in a path appear both on the right-hand side of let binders + * and in boolean path conditions. + */ + def map(f: Expr => Expr) = new Path(elements.map(_.left.map { case (id, e) => id -> f(e) }.right.map(f))) + + /** Splits the path on predicate `p` + * + * The path is split into + * 1. the sub-path containing all conditions that pass `p` (and all let-bindings), and + * 2. the sequence of conditions that didn't pass `p`. + */ + def partition(p: Expr => Boolean): (Path, Seq[Expr]) = { + val (passed, failed) = elements.partition { + case Right(e) => p(e) + case Left(_) => true + } + + (new Path(passed), failed.flatMap(_.right.toOption)) + } + + /** Check if the path is empty + * + * A path is empty iff it contains no let-bindings and its path condition is trivial. + */ + def isEmpty = elements.forall { + case Right(BooleanLiteral(true)) => true + case _ => false + } + + /** Returns the negation of a path + * + * Path negation requires folding the path into a proposition and negating it. + * However, all external let-binders can be preserved in the resulting path, thus + * avoiding let-binding dupplication in future path foldings. + */ + def negate: Path = { + val (outers, rest) = elements.span(_.isLeft) + new Path(outers :+ Right(not(fold[Expr](BooleanLiteral(true), let, Constructors.and(_, _))(rest)))) + } + + /** Returns a new path which depends ONLY on provided ids. + * + * Let-bindings obviously depend on some `id` if it corresponds to the bound + * identifier. An expression depends on an `id` iff the identifier is + * contained within the expression. + * + * This method makes little sense on its own and will typically be used from + * within a fixpoint computation where the `ids` set is iteratively computed + * by performing [[filterByIds]] calls on some (unchaning) base [[Path]]. + * + * @see [[leon.purescala.FunctionClosure.close]] for an example usecase. + */ + def filterByIds(ids: Set[Identifier]): Path = { + def containsIds(ids: Set[Identifier])(e: Expr): Boolean = exists{ + case Variable(id) => ids.contains(id) + case _ => false + }(e) + + val newElements = elements.filter{ + case Left((id, e)) => ids.contains(id) || containsIds(ids)(e) + case Right(e) => containsIds(ids)(e) + } + new Path(newElements) + } + + /** Free variables within the path */ + lazy val variables: Set[Identifier] = fold[Set[Identifier]](Set.empty, + (id, e, res) => res - id ++ variablesOf(e), (e, res) => res ++ variablesOf(e) + )(elements) + + lazy val bindings: Seq[(Identifier, Expr)] = elements.collect { case Left(p) => p } + lazy val boundIds = bindings map (_._1) + lazy val conditions: Seq[Expr] = elements.collect { case Right(e) => e } + + def isBound(id: Identifier): Boolean = bindings.exists(p => p._1 == id) + + /** Fold the path elements + * + * This function takes two combiner functions, one for let bindings and one + * for proposition expressions. + */ + private def fold[T](base: T, combineLet: (Identifier, Expr, T) => T, combineCond: (Expr, T) => T) + (elems: Seq[Element]): T = elems.foldRight(base) { + case (Left((id, e)), res) => combineLet(id, e, res) + case (Right(e), res) => combineCond(e, res) + } + + /** Folds the path elements over a distributive proposition combinator [[combine]] + * + * Certain combiners can be distributive over let-binding folds. Namely, one + * requires that `combine(let a = b in e1, e2)` be equivalent to + * `let a = b in combine(e1, e2)` (where a \not\in FV(e2)). This is the case for + * - conjunction [[and]] + * - implication [[implies]] + */ + private def distributiveClause(base: Expr, combine: (Expr, Expr) => Expr): Expr = { + val (outers, rest) = elements.span(_.isLeft) + val inner = fold[Expr](base, let, combine)(rest) + fold[Expr](inner, let, (_,_) => scala.sys.error("Should never happen!"))(outers) + } + + /** Folds the path into a conjunct with the expression `base` */ + def and(base: Expr) = distributiveClause(base, Constructors.and(_, _)) + + /** Fold the path into an implication of `base`, namely `path ==> base` */ + def implies(base: Expr) = distributiveClause(base, Constructors.implies) + + /** Folds the path into a `require` wrapping the expression `body` + * + * The function takes additional optional parameters + * - [[pre]] if one wishes to mix a pre-existing precondition into the final + * [[leon.purescala.Expressions.Require]], and + * - [[post]] for mixing a postcondition ([[leon.purescala.Expressions.Ensuring]]) in. + */ + def specs(body: Expr, pre: Expr = BooleanLiteral(true), post: Expr = NoTree(BooleanType)) = { + val (outers, rest) = elements.span(_.isLeft) + val cond = fold[Expr](BooleanLiteral(true), let, Constructors.and(_, _))(rest) + + def wrap(e: Expr): Expr = { + val bindings = rest.collect { case Left((id, e)) => id -> e } + val idSubst = bindings.map(p => p._1 -> p._1.freshen).toMap + val substMap = idSubst.mapValues(_.toVariable) + val replace = replaceFromIDs(substMap, _: Expr) + bindings.foldRight(replace(e)) { case ((id, e), b) => let(idSubst(id), replace(e), b) } + } + + val req = Require(Constructors.and(cond, wrap(pre)), wrap(body)) + val full = post match { + case l @ Lambda(args, body) => Ensuring(req, Lambda(args, wrap(body)).copiedFrom(l)) + case _ => req + } + + fold[Expr](full, let, (_, _) => scala.sys.error("Should never happen!"))(outers) + } + + /** Folds the path into the associated boolean proposition */ + lazy val toClause: Expr = and(BooleanLiteral(true)) + + /** Like [[toClause]] but doesn't simplify final path through constructors + * from [[leon.purescala.Constructors]] */ + lazy val fullClause: Expr = fold[Expr](BooleanLiteral(true), Let, And(_, _))(elements) + + /** Folds the path into a boolean proposition where let-bindings are + * replaced by equations. + * + * CAUTION: Should only be used once INSIDE the solver!! + */ + lazy val toPath: Expr = andJoin(elements.map { + case Left((id, e)) => Equals(id.toVariable, e) + case Right(e) => e + }) + + override def equals(that: Any): Boolean = that match { + case p: Path => elements == p.elements + case _ => false + } + + override def hashCode: Int = elements.hashCode + + override def toString = asString(LeonContext.printNames) + def asString(implicit ctx: LeonContext): String = fullClause.asString + def asString(pgm: Program)(implicit ctx: LeonContext): String = fullClause.asString(pgm) + } +} diff --git a/src/main/scala/inox/trees/PrettyPrinter.scala b/src/main/scala/inox/trees/PrettyPrinter.scala deleted file mode 100644 index 1d93d9725..000000000 --- a/src/main/scala/inox/trees/PrettyPrinter.scala +++ /dev/null @@ -1,756 +0,0 @@ -/* Copyright 2009-2016 EPFL, Lausanne */ - -package leon -package purescala - -import leon.utils._ -import Common._ -import DefOps._ -import Definitions._ -import Extractors._ -import PrinterHelpers._ -import ExprOps.{isListLiteral, simplestValue} -import Expressions._ -import Constructors._ -import Types._ -import org.apache.commons.lang3.StringEscapeUtils - -case class PrinterContext( - current: Tree, - parents: List[Tree], - lvl: Int, - printer: PrettyPrinter -) { - - def parent = parents.headOption -} - -/** This pretty-printer uses Unicode for some operators, to make sure we - * distinguish PureScala from "real" Scala (and also because it's cute). */ -class PrettyPrinter(opts: PrinterOptions, - opgm: Option[Program], - val sb: StringBuffer = new StringBuffer) { - - override def toString = sb.toString - - protected def optP(body: => Any)(implicit ctx: PrinterContext) = { - if (requiresParentheses(ctx.current, ctx.parent)) { - sb.append("(") - body - sb.append(")") - } else { - body - } - } - - protected def getScope(implicit ctx: PrinterContext) = - ctx.parents.collectFirst { case (d: Definition) if !d.isInstanceOf[ValDef] => d } - - protected def printNameWithPath(df: Definition)(implicit ctx: PrinterContext) { - (opgm, getScope) match { - case (Some(pgm), Some(scope)) => - sb.append(fullNameFrom(df, scope, opts.printUniqueIds)(pgm)) - - case _ => - p"${df.id}" - } - } - - private val dbquote = "\"" - - def pp(tree: Tree)(implicit ctx: PrinterContext): Unit = { - - if (requiresBraces(tree, ctx.parent) && !ctx.parent.contains(tree)) { - p"""|{ - | $tree - |}""" - return - } - - if (opts.printTypes) { - tree match { - case t: Expr => - p"⟨" - - case _ => - } - } - tree match { - case id: Identifier => - val name = if (opts.printUniqueIds) { - id.uniqueName - } else { - id.toString - } - p"$name" - - case Variable(id) => - p"$id" - - case Let(id, expr, SubString(Variable(id2), start, StringLength(Variable(id3)))) if id == id2 && id2 == id3 => - p"$expr.substring($start)" - - case Let(id, expr, BigSubString(Variable(id2), start, StringLength(Variable(id3)))) if id == id2 && id2 == id3 => - p"$expr.bigSubstring($start)" - - case Let(b,d,e) => - p"""|val $b = $d - |$e""" - - case LetDef(a::q,body) => - p"""|$a - |${letDef(q, body)}""" - case LetDef(Nil,body) => - p"""$body""" - - case Require(pre, body) => - p"""|require($pre) - |$body""" - - case Assert(const, Some(err), body) => - p"""|assert($const, "$err") - |$body""" - - case Assert(const, None, body) => - p"""|assert($const) - |$body""" - - case Ensuring(body, post) => - p"""| { - | $body - |} ensuring { - | $post - |}""" - - case p@Passes(in, out, tests) => - tests match { - case Seq(MatchCase(_, Some(BooleanLiteral(false)), NoTree(_))) => - p"""|byExample($in, $out)""" - case _ => - optP { - p"""|($in, $out) passes { - | ${nary(tests, "\n")} - |}""" - } - } - - - case c @ WithOracle(vars, pred) => - p"""|withOracle { (${typed(vars)}) => - | $pred - |}""" - - case h @ Hole(tpe, es) => - if (es.isEmpty) { - val hole = (for{scope <- getScope - program <- opgm } - yield simplifyPath("leon" :: "lang" :: "synthesis" :: "???" :: Nil, scope, false)(program)) - .getOrElse("leon.lang.synthesis.???") - p"$hole[$tpe]" - } else { - p"?($es)" - } - - case Forall(args, e) => - p"\u2200${typed(args.map(_.id))}. $e" - - case e @ CaseClass(cct, args) => - opgm.flatMap { pgm => isListLiteral(e)(pgm) } match { - case Some((tpe, elems)) => - val chars = elems.collect{ case CharLiteral(ch) => ch } - if (chars.length == elems.length && tpe == CharType) { - // String literal - val str = chars mkString "" - val q = '"' - p"$q$str$q" - } else { - val lclass = AbstractClassType(opgm.get.library.List.get, cct.tps) - - p"$lclass($elems)" - } - - case None => - if (cct.classDef.isCaseObject) { - p"$cct" - } else { - p"$cct($args)" - } - } - - case And(exprs) => optP { p"${nary(exprs, " && ")}" } - case Or(exprs) => optP { p"${nary(exprs, "| || ")}" } // Ugliness award! The first | is there to shield from stripMargin() - case Not(Equals(l, r)) => optP { p"$l \u2260 $r" } - case Implies(l,r) => optP { p"$l ==> $r" } - case BVNot(expr) => p"~$expr" - case UMinus(expr) => p"-$expr" - case BVUMinus(expr) => p"-$expr" - case RealUMinus(expr) => p"-$expr" - case Equals(l,r) => optP { p"$l == $r" } - - - case Int32ToString(expr) => p"$expr.toString" - case BooleanToString(expr) => p"$expr.toString" - case IntegerToString(expr) => p"$expr.toString" - case CharToString(expr) => p"$expr.toString" - case RealToString(expr) => p"$expr.toString" - case StringConcat(lhs, rhs) => optP { p"$lhs + $rhs" } - - case SubString(expr, start, end) => p"$expr.substring($start, $end)" - case BigSubString(expr, start, end) => p"$expr.bigSubstring($start, $end)" - case StringLength(expr) => p"$expr.length" - case StringBigLength(expr) => p"$expr.bigLength" - - case IntLiteral(v) => p"$v" - case InfiniteIntegerLiteral(v) => p"$v" - case FractionalLiteral(n, d) => - if (d == 1) p"$n" - else p"$n/$d" - case CharLiteral(v) => p"$v" - case BooleanLiteral(v) => p"$v" - case UnitLiteral() => p"()" - case StringLiteral(v) => - if(v.count(c => c == '\n') >= 1 && v.length >= 80 && v.indexOf("\"\"\"") == -1) { - p"$dbquote$dbquote$dbquote$v$dbquote$dbquote$dbquote" - } else { - val escaped = StringEscapeUtils.escapeJava(v) - p"$dbquote$escaped$dbquote" - } - case GenericValue(tp, id) => p"$tp#$id" - case Tuple(exprs) => p"($exprs)" - case TupleSelect(t, i) => p"$t._$i" - case NoTree(tpe) => p"<empty tree>[$tpe]" - case Choose(pred) => - val choose = (for{scope <- getScope - program <- opgm } - yield simplifyPath("leon" :: "lang" :: "synthesis" :: "choose" :: Nil, scope, false)(program)) - .getOrElse("leon.lang.synthesis.choose") - p"$choose($pred)" - case e @ Error(tpe, err) => p"""error[$tpe]("$err")""" - case AsInstanceOf(e, ct) => p"""$e.asInstanceOf[$ct]""" - case IsInstanceOf(e, cct) => - if (cct.classDef.isCaseObject) { - p"($e == $cct)" - } else { - p"$e.isInstanceOf[$cct]" - } - case CaseClassSelector(_, e, id) => p"$e.$id" - case MethodInvocation(rec, _, tfd, args) => - p"$rec.${tfd.id}${nary(tfd.tps, ", ", "[", "]")}" - // No () for fields - if (tfd.fd.isRealFunction) { - // The non-present arguments are synthetic function invocations - val presentArgs = args filter { - case MethodInvocation(_, _, tfd, _) if tfd.fd.isSynthetic => false - case FunctionInvocation(tfd, _) if tfd.fd.isSynthetic => false - case other => true - } - - val requireParens = presentArgs.nonEmpty || args.nonEmpty - if (requireParens) { - p"($presentArgs)" - } - } - - case BinaryMethodCall(a, op, b) => - optP { p"$a $op $b" } - - case FcallMethodInvocation(rec, fd, id, tps, args) => - - p"$rec.$id${nary(tps, ", ", "[", "]")}" - - if (fd.isRealFunction) { - // The non-present arguments are synthetic function invocations - val presentArgs = args filter { - case MethodInvocation(_, _, tfd, _) if tfd.fd.isSynthetic => false - case FunctionInvocation(tfd, _) if tfd.fd.isSynthetic => false - case other => true - } - - val requireParens = presentArgs.nonEmpty || args.nonEmpty - if (requireParens) { - p"($presentArgs)" - } - } - - case FunctionInvocation(TypedFunDef(fd, tps), args) => - printNameWithPath(fd) - - p"${nary(tps, ", ", "[", "]")}" - - if (fd.isRealFunction) { - // The non-present arguments are synthetic function invocations - val presentArgs = args filter { - case MethodInvocation(_, _, tfd, _) if tfd.fd.isSynthetic => false - case FunctionInvocation(tfd, _) if tfd.fd.isSynthetic => false - case other => true - } - val requireParens = presentArgs.nonEmpty || args.nonEmpty - if (requireParens) { - p"($presentArgs)" - } - } - - case Application(caller, args) => - p"$caller($args)" - - case Lambda(Seq(ValDef(id)), FunctionInvocation(TypedFunDef(fd, Seq()), Seq(Variable(idArg)))) if id == idArg => - printNameWithPath(fd) - - case Lambda(args, body) => - optP { p"($args) => $body" } - - case FiniteLambda(mapping, dflt, _) => - optP { - def pm(p: (Seq[Expr], Expr)): PrinterHelpers.Printable = - (pctx: PrinterContext) => p"${purescala.Constructors.tupleWrap(p._1)} => ${p._2}"(pctx) - - if (mapping.isEmpty) { - p"{ * => ${dflt} }" - } else { - p"{ ${nary(mapping map pm)}, * => ${dflt} }" - } - } - - case Plus(l,r) => optP { p"$l + $r" } - case Minus(l,r) => optP { p"$l - $r" } - case Times(l,r) => optP { p"$l * $r" } - case Division(l,r) => optP { p"$l / $r" } - case Remainder(l,r) => optP { p"$l % $r" } - case Modulo(l,r) => optP { p"$l mod $r" } - case LessThan(l,r) => optP { p"$l < $r" } - case GreaterThan(l,r) => optP { p"$l > $r" } - case LessEquals(l,r) => optP { p"$l <= $r" } - case GreaterEquals(l,r) => optP { p"$l >= $r" } - case BVPlus(l,r) => optP { p"$l + $r" } - case BVMinus(l,r) => optP { p"$l - $r" } - case BVTimes(l,r) => optP { p"$l * $r" } - case BVDivision(l,r) => optP { p"$l / $r" } - case BVRemainder(l,r) => optP { p"$l % $r" } - case BVAnd(l,r) => optP { p"$l & $r" } - case BVOr(l,r) => optP { p"$l | $r" } - case BVXOr(l,r) => optP { p"$l ^ $r" } - case BVShiftLeft(l,r) => optP { p"$l << $r" } - case BVAShiftRight(l,r) => optP { p"$l >> $r" } - case BVLShiftRight(l,r) => optP { p"$l >>> $r" } - case RealPlus(l,r) => optP { p"$l + $r" } - case RealMinus(l,r) => optP { p"$l - $r" } - case RealTimes(l,r) => optP { p"$l * $r" } - case RealDivision(l,r) => optP { p"$l / $r" } - case fs @ FiniteSet(rs, _) => p"{${rs.toSeq}}" - case fs @ FiniteBag(rs, _) => p"{$rs}" - case fm @ FiniteMap(rs, _, _) => p"{${rs.toSeq}}" - case Not(ElementOfSet(e,s)) => p"$e \u2209 $s" - case ElementOfSet(e,s) => p"$e \u2208 $s" - case SubsetOf(l,r) => p"$l \u2286 $r" - case Not(SubsetOf(l,r)) => p"$l \u2288 $r" - case SetAdd(s,e) => p"$s \u222A {$e}" - case SetUnion(l,r) => p"$l \u222A $r" - case BagUnion(l,r) => p"$l \u222A $r" - case MapUnion(l,r) => p"$l \u222A $r" - case SetDifference(l,r) => p"$l \\ $r" - case BagDifference(l,r) => p"$l \\ $r" - case SetIntersection(l,r) => p"$l \u2229 $r" - case BagIntersection(l,r) => p"$l \u2229 $r" - case SetCardinality(s) => p"$s.size" - case BagAdd(b,e) => p"$b + $e" - case MultiplicityInBag(e, b) => p"$b($e)" - case MapApply(m,k) => p"$m($k)" - case MapIsDefinedAt(m,k) => p"$m.isDefinedAt($k)" - case ArrayLength(a) => p"$a.length" - case ArraySelect(a, i) => p"$a($i)" - case ArrayUpdated(a, i, v) => p"$a.updated($i, $v)" - case a@FiniteArray(es, d, s) => { - val ArrayType(underlying) = a.getType - val default = d.getOrElse(simplestValue(underlying)) - def ppBigArray(): Unit = { - if(es.isEmpty) { - p"Array($default, $default, $default, ..., $default) (of size $s)" - } else { - p"Array(_) (of size $s)" - } - } - s match { - case IntLiteral(length) => { - if(es.size == length) { - val orderedElements = es.toSeq.sortWith((e1, e2) => e1._1 < e2._1).map(el => el._2) - p"Array($orderedElements)" - } else if(length < 10) { - val elems = (0 until length).map(i => - es.find(el => el._1 == i).map(el => el._2).getOrElse(d.get) - ) - p"Array($elems)" - } else { - ppBigArray() - } - } - case _ => ppBigArray() - } - } - - case Not(expr) => p"\u00AC$expr" - - case vd @ ValDef(id) => - if(vd.isVar) - p"var " - p"$id : ${vd.getType}" - vd.defaultValue.foreach { fd => p" = ${fd.body.get}" } - - case This(_) => p"this" - case (tfd: TypedFunDef) => p"typed def ${tfd.id}[${tfd.tps}]" - case TypeParameterDef(tp) => p"$tp" - case TypeParameter(id) => p"$id" - - - case IfExpr(c, t, ie : IfExpr) => - optP { - p"""|if ($c) { - | $t - |} else $ie""" - } - - case IfExpr(c, t, e) => - optP { - p"""|if ($c) { - | $t - |} else { - | $e - |}""" - } - - case LetPattern(p,s,rhs) => - p"""|val $p = $s - |$rhs""" - - case MatchExpr(s, csc) => - optP { - p"""|$s match { - | ${nary(csc, "\n")} - |}""" - } - - // Cases - case MatchCase(pat, optG, rhs) => - p"|case $pat "; optG foreach { g => p"if $g "}; p"""=> - | $rhs""" - - // Patterns - case WildcardPattern(None) => p"_" - case WildcardPattern(Some(id)) => p"$id" - - case CaseClassPattern(ob, cct, subps) => - ob.foreach { b => p"$b @ " } - // Print only the classDef because we don't want type parameters in patterns - printNameWithPath(cct.classDef) - if (!cct.classDef.isCaseObject) p"($subps)" - - case InstanceOfPattern(ob, cct) => - if (cct.classDef.isCaseObject) { - ob.foreach { b => p"$b @ " } - } else { - ob.foreach { b => p"$b : " } - } - // It's ok to print the whole type because there are no type parameters for case objects - p"$cct" - - case TuplePattern(ob, subps) => - ob.foreach { b => p"$b @ " } - p"($subps)" - - case UnapplyPattern(ob, tfd, subps) => - ob.foreach { b => p"$b @ " } - - // @mk: I admit this is pretty ugly - (for { - p <- opgm - mod <- p.modules.find( _.definedFunctions contains tfd.fd ) - } yield mod) match { - case Some(obj) => - printNameWithPath(obj) - case None => - p"<unknown object>" - } - - p"(${nary(subps)})" - - case LiteralPattern(ob, lit) => - ob foreach { b => p"$b @ " } - p"$lit" - - // Types - case Untyped => p"<untyped>" - case UnitType => p"Unit" - case Int32Type => p"Int" - case IntegerType => p"BigInt" - case RealType => p"Real" - case CharType => p"Char" - case BooleanType => p"Boolean" - case StringType => p"String" - case ArrayType(bt) => p"Array[$bt]" - case SetType(bt) => p"Set[$bt]" - case BagType(bt) => p"Bag[$bt]" - case MapType(ft,tt) => p"Map[$ft, $tt]" - case TupleType(tpes) => p"($tpes)" - case FunctionType(fts, tt) => p"($fts) => $tt" - case c: ClassType => - printNameWithPath(c.classDef) - p"${nary(c.tps, ", ", "[", "]")}" - - // Definitions - case Program(units) => - p"""${nary(units filter { /*opts.printUniqueIds ||*/ _.isMainUnit }, "\n\n")}""" - - case UnitDef(id,pack, imports, defs,_) => - if (pack.nonEmpty){ - p"""|package ${pack mkString "."} - |""" - } - p"""|${nary(imports,"\n")} - | - |${nary(defs,"\n\n")} - |""" - - case Import(path, isWild) => - if (isWild) { - p"import ${nary(path,".")}._" - } else { - p"import ${nary(path,".")}" - } - - case ModuleDef(id, defs, _) => - p"""|object $id { - | ${nary(defs, "\n\n")} - |}""" - - case acd : AbstractClassDef => - p"abstract class ${acd.id}${nary(acd.tparams, ", ", "[", "]")}" - - acd.parent.foreach{ par => - p" extends ${par.id}" - } - - if (acd.methods.nonEmpty) { - p"""| { - | ${nary(acd.methods, "\n\n")} - |}""" - } - - case ccd : CaseClassDef => - if (ccd.isCaseObject) { - p"case object ${ccd.id}" - } else { - p"case class ${ccd.id}" - } - - p"${nary(ccd.tparams, ", ", "[", "]")}" - - if (!ccd.isCaseObject) { - p"(${ccd.fields})" - } - - ccd.parent.foreach { par => - // Remember child and parents tparams are simple bijection - p" extends ${par.id}${nary(ccd.tparams, ", ", "[", "]")}" - } - - if (ccd.methods.nonEmpty) { - p"""| { - | ${nary(ccd.methods, "\n\n") } - |}""" - } - - case fd: FunDef => - for (an <- fd.annotations) { - p"""|@$an - |""" - } - - if (fd.canBeStrictField) { - p"val ${fd.id} : " - } else if (fd.canBeLazyField) { - p"lazy val ${fd.id} : " - } else { - p"def ${fd.id}${nary(fd.tparams, ", ", "[", "]")}(${fd.params}): " - } - - p"${fd.returnType} = ${fd.fullBody}" - - case (tree: PrettyPrintable) => tree.printWith(ctx) - - case _ => sb.append("Tree? (" + tree.getClass + ")") - } - if (opts.printTypes) { - tree match { - case t: Expr=> - p" : ${t.getType} ⟩" - - case _ => - } - } - if (opts.printPositions) { - tree.getPos match { - case op: OffsetPosition => - p"@($op)" - case rp: RangePosition => - if (rp.lineFrom == rp.lineTo) { - if (rp.colFrom == rp.colTo) { - p"@(${rp.lineFrom}:${rp.colFrom})" - } else { - p"@(${rp.lineFrom}:${rp.colFrom}-${rp.colTo})" - } - } else { - p"@(${rp.focusBegin}-${rp.focusEnd})" - } - case _ => - p"@(?)" - } - } - - } - - protected object FcallMethodInvocation { - def unapply(fi: FunctionInvocation): Option[(Expr, FunDef, Identifier, Seq[TypeTree], Seq[Expr])] = { - val FunctionInvocation(tfd, args) = fi - tfd.fd.methodOwner.map { cd => - val (rec, rargs) = (args.head, args.tail) - - val fid = tfd.fd.id - - val realtps = tfd.tps.drop(cd.tparams.size) - - (rec, tfd.fd, fid, realtps, rargs) - } - } - } - - protected object BinaryMethodCall { - val makeBinary = Set("+", "-", "*", "::", "++", "--", "&&", "||", "/") - - def unapply(fi: FunctionInvocation): Option[(Expr, String, Expr)] = fi match { - case FcallMethodInvocation(rec, _, id, Nil, List(a)) => - val name = id.name - if (makeBinary contains name) { - if(name == "::") - Some((a, name, rec)) - else - Some((rec, name, a)) - } else { - None - } - case _ => None - } - } - - protected def isSimpleExpr(e: Expr): Boolean = e match { - case _: LetDef | _: Let | LetPattern(_, _, _) | _: Assert | _: Require => false - case p: PrettyPrintable => p.isSimpleExpr - case _ => true - } - - protected def noBracesSub(e: Expr): Seq[Expr] = e match { - case Assert(_, _, bd) => Seq(bd) - case Let(_, _, bd) => Seq(bd) - case xlang.Expressions.LetVar(_, _, bd) => Seq(bd) - case LetDef(_, bd) => Seq(bd) - case LetPattern(_, _, bd) => Seq(bd) - case Require(_, bd) => Seq(bd) - case IfExpr(_, t, e) => Seq(t, e) // if-else always has braces anyway - case Ensuring(bd, pred) => Seq(bd, pred) - case _ => Seq() - } - - protected def requiresBraces(ex: Tree, within: Option[Tree]) = (ex, within) match { - case (e: Expr, _) if isSimpleExpr(e) => false - case (e: Expr, Some(within: Expr)) if noBracesSub(within) contains e => false - case (_: Expr, Some(_: MatchCase)) => false - case (_: LetDef, Some(_: LetDef)) => false - case (_: Expr, Some(_: xlang.Expressions.Block)) => false - case (_: xlang.Expressions.Block, Some(_: xlang.Expressions.While)) => false - case (_: xlang.Expressions.Block, Some(_: FunDef)) => false - case (_: xlang.Expressions.Block, Some(_: LetDef)) => false - case (e: Expr, Some(_)) => true - case _ => false - } - - protected def precedence(ex: Expr): Int = ex match { - case (pa: PrettyPrintable) => pa.printPrecedence - case (_: ElementOfSet) => 0 - case (_: Modulo) => 1 - case (_: Or | BinaryMethodCall(_, "||", _)) => 2 - case (_: And | BinaryMethodCall(_, "&&", _)) => 3 - case (_: GreaterThan | _: GreaterEquals | _: LessEquals | _: LessThan | _: Implies) => 4 - case (_: Equals | _: Not) => 5 - case (_: Plus | _: BVPlus | _: Minus | _: BVMinus | _: SetUnion| _: SetDifference | BinaryMethodCall(_, "+" | "-", _)) => 7 - case (_: Times | _: BVTimes | _: Division | _: BVDivision | _: Remainder | _: BVRemainder | BinaryMethodCall(_, "*" | "/", _)) => 8 - case _ => 9 - } - - protected def requiresParentheses(ex: Tree, within: Option[Tree]): Boolean = (ex, within) match { - case (pa: PrettyPrintable, _) => pa.printRequiresParentheses(within) - case (_, None) => false - case (_, Some( - _: Ensuring | _: Assert | _: Require | _: Definition | _: MatchExpr | _: MatchCase | - _: Let | _: LetDef | _: IfExpr | _ : CaseClass | _ : Lambda | _ : Choose | _ : Tuple - )) => false - case (_:Pattern, _) => false - case (ex: StringConcat, Some(_: StringConcat)) => false - case (b1 @ BinaryMethodCall(_, _, _), Some(b2 @ BinaryMethodCall(_, _, _))) if precedence(b1) > precedence(b2) => false - case (BinaryMethodCall(_, _, _), Some(_: FunctionInvocation)) => true - case (_, Some(_: FunctionInvocation)) => false - case (ie: IfExpr, _) => true - case (me: MatchExpr, _ ) => true - case (e1: Expr, Some(e2: Expr)) if precedence(e1) > precedence(e2) => false - case (_, _) => true - } -} - -trait PrettyPrintable { - self: Tree => - - def printWith(implicit pctx: PrinterContext): Unit - - def printPrecedence: Int = 1000 - def printRequiresParentheses(within: Option[Tree]): Boolean = false - def isSimpleExpr: Boolean = false -} - -class EquivalencePrettyPrinter(opts: PrinterOptions, opgm: Option[Program]) extends PrettyPrinter(opts, opgm) { - override def pp(tree: Tree)(implicit ctx: PrinterContext): Unit = { - tree match { - case id: Identifier => - p"${id.name}" - - case _ => - super.pp(tree) - } - } -} - -abstract class PrettyPrinterFactory { - def create(opts: PrinterOptions, opgm: Option[Program]): PrettyPrinter - - def apply(tree: Tree, opts: PrinterOptions = PrinterOptions(), opgm: Option[Program] = None): String = { - val printer = create(opts, opgm) - val ctx = PrinterContext(tree, Nil, opts.baseIndent, printer) - printer.pp(tree)(ctx) - printer.toString - } - - def apply(tree: Tree, ctx: LeonContext): String = { - val opts = PrinterOptions.fromContext(ctx) - apply(tree, opts, None) - } - - def apply(tree: Tree, ctx: LeonContext, pgm: Program): String = { - val opts = PrinterOptions.fromContext(ctx) - apply(tree, opts, Some(pgm)) - } - -} - -object PrettyPrinter extends PrettyPrinterFactory { - def create(opts: PrinterOptions, opgm: Option[Program]) = new PrettyPrinter(opts, opgm) -} - -object EquivalencePrettyPrinter extends PrettyPrinterFactory { - def create(opts: PrinterOptions, opgm: Option[Program]) = new EquivalencePrettyPrinter(opts, opgm) -} diff --git a/src/main/scala/inox/trees/PrinterHelpers.scala b/src/main/scala/inox/trees/PrinterHelpers.scala deleted file mode 100644 index 9cc470697..000000000 --- a/src/main/scala/inox/trees/PrinterHelpers.scala +++ /dev/null @@ -1,98 +0,0 @@ -/* Copyright 2009-2016 EPFL, Lausanne */ - -package leon -package purescala - -import purescala.Common._ -import purescala.Types._ - -object PrinterHelpers { - implicit class Printable(val f: PrinterContext => Any) extends AnyVal { - def print(ctx: PrinterContext) = f(ctx) - } - - implicit class PrintingHelper(val sc: StringContext) extends AnyVal { - - def p(args: Any*)(implicit ctx: PrinterContext): Unit = { - val printer = ctx.printer - val sb = printer.sb - - val strings = sc.parts.iterator - val expressions = args.iterator - - var extraInd = 0 - var firstElem = true - - while(strings.hasNext) { - val currval = strings.next - val s = if(currval != " || ") { - currval.stripMargin - } else currval - - // Compute indentation - val start = s.lastIndexOf('\n') - if(start >= 0 || firstElem) { - var i = start+1 - while(i < s.length && s(i) == ' ') { - i += 1 - } - extraInd = (i-start-1)/2 - } - - firstElem = false - - // Make sure new lines are also indented - sb.append(s.replaceAll("\n", "\n"+(" "*ctx.lvl))) - - val nctx = ctx.copy(lvl = ctx.lvl + extraInd) - - if (expressions.hasNext) { - val e = expressions.next - if(e == "||") - println("Seen Expression: "+e) - - e match { - case (t1, t2) => - nary(Seq(t1, t2), " -> ").print(nctx) - - case ts: Seq[Any] => - nary(ts).print(nctx) - - case t: Tree => - // Don't add same tree twice in parents - val parents = if (nctx.parents.headOption contains nctx.current) { - nctx.parents - } else { - nctx.current :: nctx.parents - } - val nctx2 = nctx.copy(parents = parents, current = t) - printer.pp(t)(nctx2) - - case p: Printable => - p.print(nctx) - - case e => - sb.append(e.toString) - } - } - } - } - } - - def nary(ls: Seq[Any], sep: String = ", ", init: String = "", closing: String = ""): Printable = { - val (i, c) = if(ls.isEmpty) ("", "") else (init, closing) - val strs = i +: List.fill(ls.size-1)(sep) :+ c - - implicit pctx: PrinterContext => - new StringContext(strs: _*).p(ls: _*) - } - - def typed(t: Tree with Typed): Printable = { - implicit pctx: PrinterContext => - p"$t : ${t.getType}" - } - - def typed(ts: Seq[Tree with Typed]): Printable = { - nary(ts.map(typed)) - } -} diff --git a/src/main/scala/inox/trees/Printers.scala b/src/main/scala/inox/trees/Printers.scala new file mode 100644 index 000000000..460b1bdb2 --- /dev/null +++ b/src/main/scala/inox/trees/Printers.scala @@ -0,0 +1,835 @@ +/* Copyright 2009-2016 EPFL, Lausanne */ + +package inox +package trees + +import org.apache.commons.lang3.StringEscapeUtils + +trait Printers { self: Trees => + + case class PrinterContext( + current: Tree, + parents: List[Tree], + lvl: Int, + printer: PrettyPrinter) { + + def parent = parents.headOption + } + + /** This pretty-printer uses Unicode for some operators, to make sure we + * distinguish PureScala from "real" Scala (and also because it's cute). */ + class PrettyPrinter(opts: PrinterOptions, + opgm: Option[Program], + val sb: StringBuffer = new StringBuffer) { + + override def toString = sb.toString + + protected def optP(body: => Any)(implicit ctx: PrinterContext) = { + if (requiresParentheses(ctx.current, ctx.parent)) { + sb.append("(") + body + sb.append(")") + } else { + body + } + } + + protected def getScope(implicit ctx: PrinterContext) = + ctx.parents.collectFirst { case (d: Definition) if !d.isInstanceOf[ValDef] => d } + + protected def printNameWithPath(df: Definition)(implicit ctx: PrinterContext) { + (opgm, getScope) match { + case (Some(pgm), Some(scope)) => + sb.append(fullNameFrom(df, scope, opts.printUniqueIds)(pgm)) + + case _ => + p"${df.id}" + } + } + + private val dbquote = "\"" + + def pp(tree: Tree)(implicit ctx: PrinterContext): Unit = { + + if (requiresBraces(tree, ctx.parent) && !ctx.parent.contains(tree)) { + p"""|{ + | $tree + |}""" + return + } + + if (opts.printTypes) { + tree match { + case t: Expr => + p"⟨" + + case _ => + } + } + tree match { + case id: Identifier => + val name = if (opts.printUniqueIds) { + id.uniqueName + } else { + id.toString + } + p"$name" + + case Variable(id) => + p"$id" + + case Let(id, expr, SubString(Variable(id2), start, StringLength(Variable(id3)))) if id == id2 && id2 == id3 => + p"$expr.substring($start)" + + case Let(id, expr, BigSubString(Variable(id2), start, StringLength(Variable(id3)))) if id == id2 && id2 == id3 => + p"$expr.bigSubstring($start)" + + case Let(b,d,e) => + p"""|val $b = $d + |$e""" + + case LetDef(a::q,body) => + p"""|$a + |${letDef(q, body)}""" + case LetDef(Nil,body) => + p"""$body""" + + case Require(pre, body) => + p"""|require($pre) + |$body""" + + case Assert(const, Some(err), body) => + p"""|assert($const, "$err") + |$body""" + + case Assert(const, None, body) => + p"""|assert($const) + |$body""" + + case Ensuring(body, post) => + p"""| { + | $body + |} ensuring { + | $post + |}""" + + case p @ Passes(in, out, tests) => + tests match { + case Seq(MatchCase(_, Some(BooleanLiteral(false)), NoTree(_))) => + p"""|byExample($in, $out)""" + case _ => + optP { + p"""|($in, $out) passes { + | ${nary(tests, "\n")} + |}""" + } + } + + + case c @ WithOracle(vars, pred) => + p"""|withOracle { (${typed(vars)}) => + | $pred + |}""" + + case h @ Hole(tpe, es) => + if (es.isEmpty) { + val hole = (for{scope <- getScope + program <- opgm } + yield simplifyPath("leon" :: "lang" :: "synthesis" :: "???" :: Nil, scope, false)(program)) + .getOrElse("leon.lang.synthesis.???") + p"$hole[$tpe]" + } else { + p"?($es)" + } + + case Forall(args, e) => + p"\u2200${typed(args.map(_.id))}. $e" + + case e @ CaseClass(cct, args) => + opgm.flatMap { pgm => isListLiteral(e)(pgm) } match { + case Some((tpe, elems)) => + val chars = elems.collect{ case CharLiteral(ch) => ch } + if (chars.length == elems.length && tpe == CharType) { + // String literal + val str = chars mkString "" + val q = '"' + p"$q$str$q" + } else { + val lclass = AbstractClassType(opgm.get.library.List.get, cct.tps) + + p"$lclass($elems)" + } + + case None => + if (cct.classDef.isCaseObject) { + p"$cct" + } else { + p"$cct($args)" + } + } + + case And(exprs) => optP { p"${nary(exprs, " && ")}" } + case Or(exprs) => optP { p"${nary(exprs, "| || ")}" } // Ugliness award! The first | is there to shield from stripMargin() + case Not(Equals(l, r)) => optP { p"$l \u2260 $r" } + case Implies(l,r) => optP { p"$l ==> $r" } + case BVNot(expr) => p"~$expr" + case UMinus(expr) => p"-$expr" + case BVUMinus(expr) => p"-$expr" + case RealUMinus(expr) => p"-$expr" + case Equals(l,r) => optP { p"$l == $r" } + + + case Int32ToString(expr) => p"$expr.toString" + case BooleanToString(expr) => p"$expr.toString" + case IntegerToString(expr) => p"$expr.toString" + case CharToString(expr) => p"$expr.toString" + case RealToString(expr) => p"$expr.toString" + case StringConcat(lhs, rhs) => optP { p"$lhs + $rhs" } + + case SubString(expr, start, end) => p"$expr.substring($start, $end)" + case BigSubString(expr, start, end) => p"$expr.bigSubstring($start, $end)" + case StringLength(expr) => p"$expr.length" + case StringBigLength(expr) => p"$expr.bigLength" + + case IntLiteral(v) => p"$v" + case InfiniteIntegerLiteral(v) => p"$v" + case FractionalLiteral(n, d) => + if (d == 1) p"$n" + else p"$n/$d" + case CharLiteral(v) => p"$v" + case BooleanLiteral(v) => p"$v" + case UnitLiteral() => p"()" + case StringLiteral(v) => + if(v.count(c => c == '\n') >= 1 && v.length >= 80 && v.indexOf("\"\"\"") == -1) { + p"$dbquote$dbquote$dbquote$v$dbquote$dbquote$dbquote" + } else { + val escaped = StringEscapeUtils.escapeJava(v) + p"$dbquote$escaped$dbquote" + } + case GenericValue(tp, id) => p"$tp#$id" + case Tuple(exprs) => p"($exprs)" + case TupleSelect(t, i) => p"$t._$i" + case NoTree(tpe) => p"<empty tree>[$tpe]" + case Choose(pred) => + val choose = (for{scope <- getScope + program <- opgm } + yield simplifyPath("leon" :: "lang" :: "synthesis" :: "choose" :: Nil, scope, false)(program)) + .getOrElse("leon.lang.synthesis.choose") + p"$choose($pred)" + case e @ Error(tpe, err) => p"""error[$tpe]("$err")""" + case AsInstanceOf(e, ct) => p"""$e.asInstanceOf[$ct]""" + case IsInstanceOf(e, cct) => + if (cct.classDef.isCaseObject) { + p"($e == $cct)" + } else { + p"$e.isInstanceOf[$cct]" + } + case CaseClassSelector(_, e, id) => p"$e.$id" + case MethodInvocation(rec, _, tfd, args) => + p"$rec.${tfd.id}${nary(tfd.tps, ", ", "[", "]")}" + // No () for fields + if (tfd.fd.isRealFunction) { + // The non-present arguments are synthetic function invocations + val presentArgs = args filter { + case MethodInvocation(_, _, tfd, _) if tfd.fd.isSynthetic => false + case FunctionInvocation(tfd, _) if tfd.fd.isSynthetic => false + case other => true + } + + val requireParens = presentArgs.nonEmpty || args.nonEmpty + if (requireParens) { + p"($presentArgs)" + } + } + + case BinaryMethodCall(a, op, b) => + optP { p"$a $op $b" } + + case FcallMethodInvocation(rec, fd, id, tps, args) => + + p"$rec.$id${nary(tps, ", ", "[", "]")}" + + if (fd.isRealFunction) { + // The non-present arguments are synthetic function invocations + val presentArgs = args filter { + case MethodInvocation(_, _, tfd, _) if tfd.fd.isSynthetic => false + case FunctionInvocation(tfd, _) if tfd.fd.isSynthetic => false + case other => true + } + + val requireParens = presentArgs.nonEmpty || args.nonEmpty + if (requireParens) { + p"($presentArgs)" + } + } + + case FunctionInvocation(TypedFunDef(fd, tps), args) => + printNameWithPath(fd) + + p"${nary(tps, ", ", "[", "]")}" + + if (fd.isRealFunction) { + // The non-present arguments are synthetic function invocations + val presentArgs = args filter { + case MethodInvocation(_, _, tfd, _) if tfd.fd.isSynthetic => false + case FunctionInvocation(tfd, _) if tfd.fd.isSynthetic => false + case other => true + } + val requireParens = presentArgs.nonEmpty || args.nonEmpty + if (requireParens) { + p"($presentArgs)" + } + } + + case Application(caller, args) => + p"$caller($args)" + + case Lambda(Seq(ValDef(id)), FunctionInvocation(TypedFunDef(fd, Seq()), Seq(Variable(idArg)))) if id == idArg => + printNameWithPath(fd) + + case Lambda(args, body) => + optP { p"($args) => $body" } + + case FiniteLambda(mapping, dflt, _) => + optP { + def pm(p: (Seq[Expr], Expr)): PrinterHelpers.Printable = + (pctx: PrinterContext) => p"${purescala.Constructors.tupleWrap(p._1)} => ${p._2}"(pctx) + + if (mapping.isEmpty) { + p"{ * => ${dflt} }" + } else { + p"{ ${nary(mapping map pm)}, * => ${dflt} }" + } + } + + case Plus(l,r) => optP { p"$l + $r" } + case Minus(l,r) => optP { p"$l - $r" } + case Times(l,r) => optP { p"$l * $r" } + case Division(l,r) => optP { p"$l / $r" } + case Remainder(l,r) => optP { p"$l % $r" } + case Modulo(l,r) => optP { p"$l mod $r" } + case LessThan(l,r) => optP { p"$l < $r" } + case GreaterThan(l,r) => optP { p"$l > $r" } + case LessEquals(l,r) => optP { p"$l <= $r" } + case GreaterEquals(l,r) => optP { p"$l >= $r" } + case BVPlus(l,r) => optP { p"$l + $r" } + case BVMinus(l,r) => optP { p"$l - $r" } + case BVTimes(l,r) => optP { p"$l * $r" } + case BVDivision(l,r) => optP { p"$l / $r" } + case BVRemainder(l,r) => optP { p"$l % $r" } + case BVAnd(l,r) => optP { p"$l & $r" } + case BVOr(l,r) => optP { p"$l | $r" } + case BVXOr(l,r) => optP { p"$l ^ $r" } + case BVShiftLeft(l,r) => optP { p"$l << $r" } + case BVAShiftRight(l,r) => optP { p"$l >> $r" } + case BVLShiftRight(l,r) => optP { p"$l >>> $r" } + case RealPlus(l,r) => optP { p"$l + $r" } + case RealMinus(l,r) => optP { p"$l - $r" } + case RealTimes(l,r) => optP { p"$l * $r" } + case RealDivision(l,r) => optP { p"$l / $r" } + case fs @ FiniteSet(rs, _) => p"{${rs.toSeq}}" + case fs @ FiniteBag(rs, _) => p"{$rs}" + case fm @ FiniteMap(rs, _, _) => p"{${rs.toSeq}}" + case Not(ElementOfSet(e,s)) => p"$e \u2209 $s" + case ElementOfSet(e,s) => p"$e \u2208 $s" + case SubsetOf(l,r) => p"$l \u2286 $r" + case Not(SubsetOf(l,r)) => p"$l \u2288 $r" + case SetAdd(s,e) => p"$s \u222A {$e}" + case SetUnion(l,r) => p"$l \u222A $r" + case BagUnion(l,r) => p"$l \u222A $r" + case MapUnion(l,r) => p"$l \u222A $r" + case SetDifference(l,r) => p"$l \\ $r" + case BagDifference(l,r) => p"$l \\ $r" + case SetIntersection(l,r) => p"$l \u2229 $r" + case BagIntersection(l,r) => p"$l \u2229 $r" + case SetCardinality(s) => p"$s.size" + case BagAdd(b,e) => p"$b + $e" + case MultiplicityInBag(e, b) => p"$b($e)" + case MapApply(m,k) => p"$m($k)" + case MapIsDefinedAt(m,k) => p"$m.isDefinedAt($k)" + case ArrayLength(a) => p"$a.length" + case ArraySelect(a, i) => p"$a($i)" + case ArrayUpdated(a, i, v) => p"$a.updated($i, $v)" + case a@FiniteArray(es, d, s) => { + val ArrayType(underlying) = a.getType + val default = d.getOrElse(simplestValue(underlying)) + def ppBigArray(): Unit = { + if(es.isEmpty) { + p"Array($default, $default, $default, ..., $default) (of size $s)" + } else { + p"Array(_) (of size $s)" + } + } + s match { + case IntLiteral(length) => { + if(es.size == length) { + val orderedElements = es.toSeq.sortWith((e1, e2) => e1._1 < e2._1).map(el => el._2) + p"Array($orderedElements)" + } else if(length < 10) { + val elems = (0 until length).map(i => + es.find(el => el._1 == i).map(el => el._2).getOrElse(d.get) + ) + p"Array($elems)" + } else { + ppBigArray() + } + } + case _ => ppBigArray() + } + } + + case Not(expr) => p"\u00AC$expr" + + case vd @ ValDef(id) => + if(vd.isVar) + p"var " + p"$id : ${vd.getType}" + vd.defaultValue.foreach { fd => p" = ${fd.body.get}" } + + case This(_) => p"this" + case (tfd: TypedFunDef) => p"typed def ${tfd.id}[${tfd.tps}]" + case TypeParameterDef(tp) => p"$tp" + case TypeParameter(id) => p"$id" + + + case IfExpr(c, t, ie : IfExpr) => + optP { + p"""|if ($c) { + | $t + |} else $ie""" + } + + case IfExpr(c, t, e) => + optP { + p"""|if ($c) { + | $t + |} else { + | $e + |}""" + } + + case LetPattern(p,s,rhs) => + p"""|val $p = $s + |$rhs""" + + case MatchExpr(s, csc) => + optP { + p"""|$s match { + | ${nary(csc, "\n")} + |}""" + } + + // Cases + case MatchCase(pat, optG, rhs) => + p"|case $pat "; optG foreach { g => p"if $g "}; p"""=> + | $rhs""" + + // Patterns + case WildcardPattern(None) => p"_" + case WildcardPattern(Some(id)) => p"$id" + + case CaseClassPattern(ob, cct, subps) => + ob.foreach { b => p"$b @ " } + // Print only the classDef because we don't want type parameters in patterns + printNameWithPath(cct.classDef) + if (!cct.classDef.isCaseObject) p"($subps)" + + case InstanceOfPattern(ob, cct) => + if (cct.classDef.isCaseObject) { + ob.foreach { b => p"$b @ " } + } else { + ob.foreach { b => p"$b : " } + } + // It's ok to print the whole type because there are no type parameters for case objects + p"$cct" + + case TuplePattern(ob, subps) => + ob.foreach { b => p"$b @ " } + p"($subps)" + + case UnapplyPattern(ob, tfd, subps) => + ob.foreach { b => p"$b @ " } + + // @mk: I admit this is pretty ugly + (for { + p <- opgm + mod <- p.modules.find( _.definedFunctions contains tfd.fd ) + } yield mod) match { + case Some(obj) => + printNameWithPath(obj) + case None => + p"<unknown object>" + } + + p"(${nary(subps)})" + + case LiteralPattern(ob, lit) => + ob foreach { b => p"$b @ " } + p"$lit" + + // Types + case Untyped => p"<untyped>" + case UnitType => p"Unit" + case Int32Type => p"Int" + case IntegerType => p"BigInt" + case RealType => p"Real" + case CharType => p"Char" + case BooleanType => p"Boolean" + case StringType => p"String" + case ArrayType(bt) => p"Array[$bt]" + case SetType(bt) => p"Set[$bt]" + case BagType(bt) => p"Bag[$bt]" + case MapType(ft,tt) => p"Map[$ft, $tt]" + case TupleType(tpes) => p"($tpes)" + case FunctionType(fts, tt) => p"($fts) => $tt" + case c: ClassType => + printNameWithPath(c.classDef) + p"${nary(c.tps, ", ", "[", "]")}" + + // Definitions + case Program(units) => + p"""${nary(units filter { /*opts.printUniqueIds ||*/ _.isMainUnit }, "\n\n")}""" + + case UnitDef(id,pack, imports, defs,_) => + if (pack.nonEmpty){ + p"""|package ${pack mkString "."} + |""" + } + p"""|${nary(imports,"\n")} + | + |${nary(defs,"\n\n")} + |""" + + case Import(path, isWild) => + if (isWild) { + p"import ${nary(path,".")}._" + } else { + p"import ${nary(path,".")}" + } + + case ModuleDef(id, defs, _) => + p"""|object $id { + | ${nary(defs, "\n\n")} + |}""" + + case acd : AbstractClassDef => + p"abstract class ${acd.id}${nary(acd.tparams, ", ", "[", "]")}" + + acd.parent.foreach{ par => + p" extends ${par.id}" + } + + if (acd.methods.nonEmpty) { + p"""| { + | ${nary(acd.methods, "\n\n")} + |}""" + } + + case ccd : CaseClassDef => + if (ccd.isCaseObject) { + p"case object ${ccd.id}" + } else { + p"case class ${ccd.id}" + } + + p"${nary(ccd.tparams, ", ", "[", "]")}" + + if (!ccd.isCaseObject) { + p"(${ccd.fields})" + } + + ccd.parent.foreach { par => + // Remember child and parents tparams are simple bijection + p" extends ${par.id}${nary(ccd.tparams, ", ", "[", "]")}" + } + + if (ccd.methods.nonEmpty) { + p"""| { + | ${nary(ccd.methods, "\n\n") } + |}""" + } + + case fd: FunDef => + for (an <- fd.annotations) { + p"""|@$an + |""" + } + + if (fd.canBeStrictField) { + p"val ${fd.id} : " + } else if (fd.canBeLazyField) { + p"lazy val ${fd.id} : " + } else { + p"def ${fd.id}${nary(fd.tparams, ", ", "[", "]")}(${fd.params}): " + } + + p"${fd.returnType} = ${fd.fullBody}" + + case (tree: PrettyPrintable) => tree.printWith(ctx) + + case _ => sb.append("Tree? (" + tree.getClass + ")") + } + if (opts.printTypes) { + tree match { + case t: Expr=> + p" : ${t.getType} ⟩" + + case _ => + } + } + if (opts.printPositions) { + tree.getPos match { + case op: OffsetPosition => + p"@($op)" + case rp: RangePosition => + if (rp.lineFrom == rp.lineTo) { + if (rp.colFrom == rp.colTo) { + p"@(${rp.lineFrom}:${rp.colFrom})" + } else { + p"@(${rp.lineFrom}:${rp.colFrom}-${rp.colTo})" + } + } else { + p"@(${rp.focusBegin}-${rp.focusEnd})" + } + case _ => + p"@(?)" + } + } + } + + protected object FcallMethodInvocation { + def unapply(fi: FunctionInvocation): Option[(Expr, FunDef, Identifier, Seq[TypeTree], Seq[Expr])] = { + val FunctionInvocation(tfd, args) = fi + tfd.fd.methodOwner.map { cd => + val (rec, rargs) = (args.head, args.tail) + + val fid = tfd.fd.id + + val realtps = tfd.tps.drop(cd.tparams.size) + + (rec, tfd.fd, fid, realtps, rargs) + } + } + } + + protected object BinaryMethodCall { + val makeBinary = Set("+", "-", "*", "::", "++", "--", "&&", "||", "/") + + def unapply(fi: FunctionInvocation): Option[(Expr, String, Expr)] = fi match { + case FcallMethodInvocation(rec, _, id, Nil, List(a)) => + val name = id.name + if (makeBinary contains name) { + if(name == "::") + Some((a, name, rec)) + else + Some((rec, name, a)) + } else { + None + } + case _ => None + } + } + + protected def isSimpleExpr(e: Expr): Boolean = e match { + case _: LetDef | _: Let | LetPattern(_, _, _) | _: Assert | _: Require => false + case p: PrettyPrintable => p.isSimpleExpr + case _ => true + } + + protected def noBracesSub(e: Expr): Seq[Expr] = e match { + case Assert(_, _, bd) => Seq(bd) + case Let(_, _, bd) => Seq(bd) + case xlang.Expressions.LetVar(_, _, bd) => Seq(bd) + case LetDef(_, bd) => Seq(bd) + case LetPattern(_, _, bd) => Seq(bd) + case Require(_, bd) => Seq(bd) + case IfExpr(_, t, e) => Seq(t, e) // if-else always has braces anyway + case Ensuring(bd, pred) => Seq(bd, pred) + case _ => Seq() + } + + protected def requiresBraces(ex: Tree, within: Option[Tree]) = (ex, within) match { + case (e: Expr, _) if isSimpleExpr(e) => false + case (e: Expr, Some(within: Expr)) if noBracesSub(within) contains e => false + case (_: Expr, Some(_: MatchCase)) => false + case (_: LetDef, Some(_: LetDef)) => false + case (_: Expr, Some(_: xlang.Expressions.Block)) => false + case (_: xlang.Expressions.Block, Some(_: xlang.Expressions.While)) => false + case (_: xlang.Expressions.Block, Some(_: FunDef)) => false + case (_: xlang.Expressions.Block, Some(_: LetDef)) => false + case (e: Expr, Some(_)) => true + case _ => false + } + + protected def precedence(ex: Expr): Int = ex match { + case (pa: PrettyPrintable) => pa.printPrecedence + case (_: ElementOfSet) => 0 + case (_: Modulo) => 1 + case (_: Or | BinaryMethodCall(_, "||", _)) => 2 + case (_: And | BinaryMethodCall(_, "&&", _)) => 3 + case (_: GreaterThan | _: GreaterEquals | _: LessEquals | _: LessThan | _: Implies) => 4 + case (_: Equals | _: Not) => 5 + case (_: Plus | _: BVPlus | _: Minus | _: BVMinus | _: SetUnion| _: SetDifference | BinaryMethodCall(_, "+" | "-", _)) => 7 + case (_: Times | _: BVTimes | _: Division | _: BVDivision | _: Remainder | _: BVRemainder | BinaryMethodCall(_, "*" | "/", _)) => 8 + case _ => 9 + } + + protected def requiresParentheses(ex: Tree, within: Option[Tree]): Boolean = (ex, within) match { + case (pa: PrettyPrintable, _) => pa.printRequiresParentheses(within) + case (_, None) => false + case (_, Some( + _: Ensuring | _: Assert | _: Require | _: Definition | _: MatchExpr | _: MatchCase | + _: Let | _: LetDef | _: IfExpr | _ : CaseClass | _ : Lambda | _ : Choose | _ : Tuple + )) => false + case (_:Pattern, _) => false + case (ex: StringConcat, Some(_: StringConcat)) => false + case (b1 @ BinaryMethodCall(_, _, _), Some(b2 @ BinaryMethodCall(_, _, _))) if precedence(b1) > precedence(b2) => false + case (BinaryMethodCall(_, _, _), Some(_: FunctionInvocation)) => true + case (_, Some(_: FunctionInvocation)) => false + case (ie: IfExpr, _) => true + case (me: MatchExpr, _ ) => true + case (e1: Expr, Some(e2: Expr)) if precedence(e1) > precedence(e2) => false + case (_, _) => true + } + } + + implicit class Printable(val f: PrinterContext => Any) extends AnyVal { + def print(ctx: PrinterContext) = f(ctx) + } + + implicit class PrintingHelper(val sc: StringContext) extends AnyVal { + + def p(args: Any*)(implicit ctx: PrinterContext): Unit = { + val printer = ctx.printer + val sb = printer.sb + + val strings = sc.parts.iterator + val expressions = args.iterator + + var extraInd = 0 + var firstElem = true + + while(strings.hasNext) { + val currval = strings.next + val s = if(currval != " || ") { + currval.stripMargin + } else currval + + // Compute indentation + val start = s.lastIndexOf('\n') + if(start >= 0 || firstElem) { + var i = start+1 + while(i < s.length && s(i) == ' ') { + i += 1 + } + extraInd = (i-start-1)/2 + } + + firstElem = false + + // Make sure new lines are also indented + sb.append(s.replaceAll("\n", "\n"+(" "*ctx.lvl))) + + val nctx = ctx.copy(lvl = ctx.lvl + extraInd) + + if (expressions.hasNext) { + val e = expressions.next + if(e == "||") + println("Seen Expression: "+e) + + e match { + case (t1, t2) => + nary(Seq(t1, t2), " -> ").print(nctx) + + case ts: Seq[Any] => + nary(ts).print(nctx) + + case t: Tree => + // Don't add same tree twice in parents + val parents = if (nctx.parents.headOption contains nctx.current) { + nctx.parents + } else { + nctx.current :: nctx.parents + } + val nctx2 = nctx.copy(parents = parents, current = t) + printer.pp(t)(nctx2) + + case p: Printable => + p.print(nctx) + + case e => + sb.append(e.toString) + } + } + } + } + } + + def nary(ls: Seq[Any], sep: String = ", ", init: String = "", closing: String = ""): Printable = { + val (i, c) = if(ls.isEmpty) ("", "") else (init, closing) + val strs = i +: List.fill(ls.size-1)(sep) :+ c + + implicit pctx: PrinterContext => + new StringContext(strs: _*).p(ls: _*) + } + + def typed(t: Tree with Typed): Printable = { + implicit pctx: PrinterContext => + p"$t : ${t.getType}" + } + + def typed(ts: Seq[Tree with Typed]): Printable = { + nary(ts.map(typed)) + } + + trait PrettyPrintable { + self: Tree => + + def printWith(implicit pctx: PrinterContext): Unit + + def printPrecedence: Int = 1000 + def printRequiresParentheses(within: Option[Tree]): Boolean = false + def isSimpleExpr: Boolean = false + } + + class EquivalencePrettyPrinter(opts: PrinterOptions, opgm: Option[Program]) extends PrettyPrinter(opts, opgm) { + override def pp(tree: Tree)(implicit ctx: PrinterContext): Unit = { + tree match { + case id: Identifier => + p"${id.name}" + + case _ => + super.pp(tree) + } + } + } + + abstract class PrettyPrinterFactory { + def create(opts: PrinterOptions, opgm: Option[Program]): PrettyPrinter + + def apply(tree: Tree, opts: PrinterOptions = PrinterOptions(), opgm: Option[Program] = None): String = { + val printer = create(opts, opgm) + val ctx = PrinterContext(tree, Nil, opts.baseIndent, printer) + printer.pp(tree)(ctx) + printer.toString + } + + def apply(tree: Tree, ctx: LeonContext): String = { + val opts = PrinterOptions.fromContext(ctx) + apply(tree, opts, None) + } + + def apply(tree: Tree, ctx: LeonContext, pgm: Program): String = { + val opts = PrinterOptions.fromContext(ctx) + apply(tree, opts, Some(pgm)) + } + } + + object PrettyPrinter extends PrettyPrinterFactory { + def create(opts: PrinterOptions, opgm: Option[Program]) = new PrettyPrinter(opts, opgm) + } + + object EquivalencePrettyPrinter extends PrettyPrinterFactory { + def create(opts: PrinterOptions, opgm: Option[Program]) = new EquivalencePrettyPrinter(opts, opgm) + } +} diff --git a/src/main/scala/inox/trees/Transformer.scala b/src/main/scala/inox/trees/Transformer.scala deleted file mode 100644 index c44d56a67..000000000 --- a/src/main/scala/inox/trees/Transformer.scala +++ /dev/null @@ -1,10 +0,0 @@ -/* Copyright 2009-2016 EPFL, Lausanne */ - -package leon -package purescala - -import purescala.Expressions.Expr - -trait Transformer { - def transform(e: Expr): Expr -} diff --git a/src/main/scala/inox/trees/TransformerWithPC.scala b/src/main/scala/inox/trees/TransformerWithPC.scala deleted file mode 100644 index 2408757a6..000000000 --- a/src/main/scala/inox/trees/TransformerWithPC.scala +++ /dev/null @@ -1,101 +0,0 @@ -/* Copyright 2009-2016 EPFL, Lausanne */ - -package leon -package purescala - -import Expressions._ -import Constructors._ -import Extractors._ -import ExprOps._ - -/** Traverses expressions with path condition awareness. - * - * As lets cannot be encoded as Equals due to types for which equality - * is not well-founded, path conditions reconstruct lets around the - * final condition one wishes to verify through [[Path.getClause]]. - */ -abstract class TraverserWithPaths[T](trees: Trees) { - import trees._ - - protected def rec(e: Expr, path: Path): Unit = e match { - case Let(i, v, b) => - val se = rec(v, path) - val sb = rec(b, path withBinding (i -> se)) - Let(i, se, sb).copiedFrom(e) - - case Ensuring(req @ Require(pre, body), lam @ Lambda(Seq(arg), post)) => - val spre = rec(pre, path) - val sbody = rec(body, path withCond spre) - val spost = rec(post, path withCond spre withBinding (arg.id -> sbody)) - Ensuring( - Require(spre, sbody).copiedFrom(req), - Lambda(Seq(arg), spost).copiedFrom(lam) - ).copiedFrom(e) - - case Ensuring(body, lam @ Lambda(Seq(arg), post)) => - val sbody = rec(body, path) - val spost = rec(post, path withBinding (arg.id -> sbody)) - Ensuring( - sbody, - Lambda(Seq(arg), spost).copiedFrom(lam) - ).copiedFrom(e) - - case Require(pre, body) => - val sp = rec(pre, path) - val sb = rec(body, path withCond pre) - Require(sp, sb).copiedFrom(e) - - case Assert(pred, err, body) => - val sp = rec(pred, path) - val sb = rec(body, register(sp, path)) - Assert(sp, err, sb).copiedFrom(e) - - case MatchExpr(scrut, cases) => - val rs = rec(scrut, path) - var soFar = path - - MatchExpr(rs, cases.map { c => - val patternPathPos = conditionForPattern(rs, c.pattern, includeBinders = true) - val patternPathNeg = conditionForPattern(rs, c.pattern, includeBinders = false) - val map = mapForPattern(rs, c.pattern) - val guardOrTrue = c.optGuard.getOrElse(BooleanLiteral(true)) - val guardMapped = replaceFromIDs(map, guardOrTrue) - - val subPath = soFar merge (patternPathPos withCond guardOrTrue) - soFar = soFar merge (patternPathNeg withCond guardMapped).negate - - MatchCase(c.pattern, c.optGuard, rec(c.rhs, subPath)).copiedFrom(c) - }).copiedFrom(e) - - case IfExpr(cond, thenn, elze) => - val rc = rec(cond, path) - IfExpr(rc, rec(thenn, path withCond rc), rec(elze, path withCond Not(rc))).copiedFrom(e) - - case And(es) => - var soFar = path - andJoin(for(e <- es) yield { - val se = rec(e, soFar) - soFar = soFar withCond se - se - }).copiedFrom(e) - - case Or(es) => - var soFar = path - orJoin(for(e <- es) yield { - val se = rec(e, soFar) - soFar = soFar withCond Not(se) - se - }).copiedFrom(e) - - case i @ Implies(lhs, rhs) => - val rc = rec(lhs, path) - Implies(rc, rec(rhs, path withCond rc)).copiedFrom(i) - - case o @ Operator(es, builder) => - builder(es.map(rec(_, path))).copiedFrom(o) - - case _ => - sys.error("Expression "+e+" ["+e.getClass+"] is not extractable") - } -} - diff --git a/src/main/scala/inox/trees/TreeTransformer.scala b/src/main/scala/inox/trees/TreeTransformer.scala deleted file mode 100644 index a1f6e2ad6..000000000 --- a/src/main/scala/inox/trees/TreeTransformer.scala +++ /dev/null @@ -1,242 +0,0 @@ -/* Copyright 2009-2015 EPFL, Lausanne */ - -package leon -package purescala - -import Common._ -import Definitions._ -import Expressions._ -import xlang.Expressions._ -import Extractors._ -import Types._ - -import utils._ -import scala.collection.mutable.{Map => MutableMap, Set => MutableSet} - -trait TreeTransformer { - def transform(id: Identifier): Identifier = id - def transform(cd: ClassDef): ClassDef = cd - def transform(fd: FunDef): FunDef = fd - - def transform(e: Expr)(implicit bindings: Map[Identifier, Identifier]): Expr = e match { - case Variable(id) if bindings contains id => Variable(bindings(id)).copiedFrom(e) - case Variable(id) => Variable(transform(id)).copiedFrom(e) - case FiniteLambda(mappings, default, tpe) => - FiniteLambda(mappings.map { case (ks, v) => (ks map transform, transform(v)) }, - transform(default), transform(tpe).asInstanceOf[FunctionType]).copiedFrom(e) - case Lambda(args, body) => - val newArgs = args.map(vd => ValDef(transform(vd.id))) - val newBindings = (args zip newArgs).map(p => p._1.id -> p._2.id) - Lambda(newArgs, transform(body)(bindings ++ newBindings)).copiedFrom(e) - case Forall(args, body) => - val newArgs = args.map(vd => ValDef(transform(vd.id))) - val newBindings = (args zip newArgs).map(p => p._1.id -> p._2.id) - Forall(newArgs, transform(body)(bindings ++ newBindings)).copiedFrom(e) - case Let(a, expr, body) => - val newA = transform(a) - Let(newA, transform(expr), transform(body)(bindings + (a -> newA))).copiedFrom(e) - case LetVar(a, expr, body) => - val newA = transform(a) - LetVar(newA, transform(expr), transform(body)(bindings + (a -> newA))).copiedFrom(e) - case LetDef(fds, body) => - val rFds = fds map transform - val rBody = transform(body) - LetDef(rFds, rBody).copiedFrom(e) - case CaseClass(cct, args) => - CaseClass(transform(cct).asInstanceOf[CaseClassType], args map transform).copiedFrom(e) - case CaseClassSelector(cct, caseClass, selector) => - val newCct @ CaseClassType(ccd, _) = transform(cct) - val newSelector = ccd.fieldsIds(cct.classDef.fieldsIds.indexOf(selector)) - CaseClassSelector(newCct, transform(caseClass), newSelector).copiedFrom(e) - case FunctionInvocation(TypedFunDef(fd, tpes), args) => - FunctionInvocation(TypedFunDef(transform(fd), tpes map transform), args map transform).copiedFrom(e) - case MethodInvocation(rec, cd, TypedFunDef(fd, tpes), args) => - MethodInvocation(transform(rec), transform(cd), TypedFunDef(transform(fd), tpes map transform), args map transform).copiedFrom(e) - case This(ct) => - This(transform(ct).asInstanceOf[ClassType]).copiedFrom(e) - case IsInstanceOf(expr, ct) => - IsInstanceOf(transform(expr), transform(ct).asInstanceOf[ClassType]).copiedFrom(e) - case AsInstanceOf(expr, ct) => - AsInstanceOf(transform(expr), transform(ct).asInstanceOf[ClassType]).copiedFrom(e) - case MatchExpr(scrutinee, cases) => - MatchExpr(transform(scrutinee), for (cse @ MatchCase(pattern, guard, rhs) <- cases) yield { - val (newPattern, newBindings) = transform(pattern) - val allBindings = bindings ++ newBindings - MatchCase(newPattern, guard.map(g => transform(g)(allBindings)), transform(rhs)(allBindings)).copiedFrom(cse) - }).copiedFrom(e) - case Passes(in, out, cases) => - Passes(transform(in), transform(out), for (cse @ MatchCase(pattern, guard, rhs) <- cases) yield { - val (newPattern, newBindings) = transform(pattern) - val allBindings = bindings ++ newBindings - MatchCase(newPattern, guard.map(g => transform(g)(allBindings)), transform(rhs)(allBindings)).copiedFrom(cse) - }).copiedFrom(e) - case FiniteSet(es, tpe) => - FiniteSet(es map transform, transform(tpe)).copiedFrom(e) - case FiniteBag(es, tpe) => - FiniteBag(es map { case (k, v) => transform(k) -> v }, transform(tpe)).copiedFrom(e) - case FiniteMap(pairs, from, to) => - FiniteMap(pairs map { case (k, v) => transform(k) -> transform(v) }, transform(from), transform(to)).copiedFrom(e) - case EmptyArray(tpe) => - EmptyArray(transform(tpe)).copiedFrom(e) - case Hole(tpe, alts) => - Hole(transform(tpe), alts map transform).copiedFrom(e) - case NoTree(tpe) => - NoTree(transform(tpe)).copiedFrom(e) - case Error(tpe, desc) => - Error(transform(tpe), desc).copiedFrom(e) - case Operator(es, builder) => - val newEs = es map transform - if ((newEs zip es).exists { case (bef, aft) => aft ne bef }) { - builder(newEs).copiedFrom(e) - } else { - e - } - case e => e - } - - def transform(pat: Pattern): (Pattern, Map[Identifier, Identifier]) = pat match { - case InstanceOfPattern(binder, ct) => - val newBinder = binder map transform - val newPat = InstanceOfPattern(newBinder, transform(ct).asInstanceOf[ClassType]).copiedFrom(pat) - (newPat, (binder zip newBinder).toMap) - case WildcardPattern(binder) => - val newBinder = binder map transform - val newPat = WildcardPattern(newBinder).copiedFrom(pat) - (newPat, (binder zip newBinder).toMap) - case CaseClassPattern(binder, ct, subs) => - val newBinder = binder map transform - val (newSubs, subBinders) = (subs map transform).unzip - val newPat = CaseClassPattern(newBinder, transform(ct).asInstanceOf[CaseClassType], newSubs).copiedFrom(pat) - (newPat, (binder zip newBinder).toMap ++ subBinders.flatten) - case TuplePattern(binder, subs) => - val newBinder = binder map transform - val (newSubs, subBinders) = (subs map transform).unzip - val newPat = TuplePattern(newBinder, newSubs).copiedFrom(pat) - (newPat, (binder zip newBinder).toMap ++ subBinders.flatten) - case UnapplyPattern(binder, TypedFunDef(fd, tpes), subs) => - val newBinder = binder map transform - val (newSubs, subBinders) = (subs map transform).unzip - val newPat = UnapplyPattern(newBinder, TypedFunDef(transform(fd), tpes map transform), newSubs).copiedFrom(pat) - (newPat, (binder zip newBinder).toMap ++ subBinders.flatten) - case PatternExtractor(subs, builder) => - val (newSubs, subBinders) = (subs map transform).unzip - (builder(newSubs).copiedFrom(pat), subBinders.flatten.toMap) - } - - def transform(tpe: TypeTree): TypeTree = tpe match { - case cct @ CaseClassType(ccd, args) => - CaseClassType(transform(ccd).asInstanceOf[CaseClassDef], args map transform).copiedFrom(tpe) - case act @ AbstractClassType(acd, args) => - AbstractClassType(transform(acd).asInstanceOf[AbstractClassDef], args map transform).copiedFrom(tpe) - case NAryType(ts, builder) => builder(ts map transform).copiedFrom(tpe) - } -} - -trait TreeTraverser { - def traverse(id: Identifier): Unit = () - def traverse(cd: ClassDef): Unit = () - def traverse(fd: FunDef): Unit = () - - def traverse(e: Expr): Unit = e match { - case Variable(id) => traverse(id) - case FiniteLambda(mappings, default, tpe) => - (default +: mappings.toSeq.flatMap(p => p._2 +: p._1)) foreach traverse - traverse(tpe) - case Lambda(args, body) => - args foreach (vd => traverse(vd.id)) - traverse(body) - case Forall(args, body) => - args foreach (vd => traverse(vd.id)) - traverse(body) - case Let(a, expr, body) => - traverse(a) - traverse(expr) - traverse(body) - case CaseClass(cct, args) => - traverse(cct) - args foreach traverse - case CaseClassSelector(cct, caseClass, selector) => - traverse(cct) - traverse(caseClass) - case FunctionInvocation(TypedFunDef(fd, tpes), args) => - traverse(fd) - tpes foreach traverse - args foreach traverse - case MethodInvocation(rec, cd, TypedFunDef(fd, tpes), args) => - traverse(rec) - traverse(cd) - traverse(fd) - tpes foreach traverse - args foreach traverse - case This(ct) => - traverse(ct) - case IsInstanceOf(expr, ct) => - traverse(expr) - traverse(ct) - case AsInstanceOf(expr, ct) => - traverse(expr) - traverse(ct) - case MatchExpr(scrutinee, cases) => - traverse(scrutinee) - for (cse @ MatchCase(pattern, guard, rhs) <- cases) { - traverse(pattern) - guard foreach traverse - traverse(rhs) - } - case FiniteSet(es, tpe) => - es foreach traverse - traverse(tpe) - case FiniteBag(es, tpe) => - es foreach { case (k, _) => traverse(k) } - traverse(tpe) - case FiniteMap(pairs, from, to) => - pairs foreach { case (k, v) => traverse(k); traverse(v) } - traverse(from) - traverse(to) - case EmptyArray(tpe) => - traverse(tpe) - case Hole(tpe, alts) => - traverse(tpe) - alts foreach traverse - case NoTree(tpe) => - traverse(tpe) - case Error(tpe, desc) => - traverse(tpe) - case Operator(es, builder) => - es foreach traverse - case e => - } - - def traverse(pat: Pattern): Unit = pat match { - case InstanceOfPattern(binder, ct) => - binder foreach traverse - traverse(ct) - case WildcardPattern(binder) => - binder foreach traverse - case CaseClassPattern(binder, ct, subs) => - binder foreach traverse - traverse(ct) - subs foreach traverse - case TuplePattern(binder, subs) => - binder foreach traverse - subs foreach traverse - case UnapplyPattern(binder, TypedFunDef(fd, tpes), subs) => - binder foreach traverse - traverse(fd) - tpes foreach traverse - subs foreach traverse - case PatternExtractor(subs, builder) => - subs foreach traverse - } - - def traverse(tpe: TypeTree): Unit = tpe match { - case cct @ CaseClassType(ccd, args) => - traverse(ccd) - args foreach traverse - case act @ AbstractClassType(acd, args) => - traverse(acd) - args foreach traverse - case NAryType(ts, builder) => - ts foreach traverse - } -} diff --git a/src/main/scala/inox/trees/Trees.scala b/src/main/scala/inox/trees/Trees.scala index 39d317891..5b7874b18 100644 --- a/src/main/scala/inox/trees/Trees.scala +++ b/src/main/scala/inox/trees/Trees.scala @@ -1,14 +1,17 @@ /* Copyright 2009-2016 EPFL, Lausanne */ -package leon -package purescala +package inox +package trees -import utils._ -import Expressions.Variable -import Types._ -import Definitions.Program +trait Trees extends Expressions with Extractors with Types with Definitions { -trait Trees extends Expressions with ExprOps with Types with TypeOps { + object exprOps extends { + val trees: Trees.this.type = Trees.this + } with ExprOps with Constructors + + object typeOps extends { + val trees: Trees.this.type = Trees.this + } with TypeOps abstract class Tree extends Positioned with Serializable with Printable { def copiedFrom(o: Tree): this.type = { @@ -34,11 +37,8 @@ trait Trees extends Expressions with ExprOps with Types with TypeOps { val name: String, val globalId: Int, private[Common] val id: Int, - private val tpe: TypeTree, private val alwaysShowUniqueID: Boolean = false - ) extends Tree with Typed with Ordered[Identifier] { - - val getType = tpe + ) extends Tree with Ordered[Identifier] { override def equals(other: Any): Boolean = other match { case null => false diff --git a/src/main/scala/inox/trees/TypeOps.scala b/src/main/scala/inox/trees/TypeOps.scala index 2a9e96b2d..0fd4bf588 100644 --- a/src/main/scala/inox/trees/TypeOps.scala +++ b/src/main/scala/inox/trees/TypeOps.scala @@ -1,19 +1,18 @@ /* Copyright 2009-2016 EPFL, Lausanne */ -package leon -package purescala +package inox +package trees -import Types._ -import Definitions._ -import Common._ -import Expressions._ +trait TypeOps extends GenTreeOps { + val trees: Trees + import trees._ -object TypeOps extends GenTreeOps[TypeTree] { + type SubTree = Type val Deconstructor = NAryType def typeParamsOf(expr: Expr): Set[TypeParameter] = { - ExprOps.collect(e => typeParamsOf(e.getType))(expr) + exprOps.collect(e => typeParamsOf(e.getType))(expr) } def typeParamsOf(t: TypeTree): Set[TypeParameter] = t match { diff --git a/src/main/scala/inox/trees/Types.scala b/src/main/scala/inox/trees/Types.scala index 608f01ccf..fe9d6a0bb 100644 --- a/src/main/scala/inox/trees/Types.scala +++ b/src/main/scala/inox/trees/Types.scala @@ -1,12 +1,7 @@ /* Copyright 2009-2016 EPFL, Lausanne */ -package leon -package purescala - -import Common._ -import Expressions._ -import Definitions._ -import TypeOps._ +package inox +package trees trait Types { self: Trees => @@ -89,7 +84,12 @@ trait Types { self: Trees => def lookupClass(implicit p: Program): Option[ClassDef] = p.lookupClass(id, tps) } - object NAryType extends TreeExtractor[Type] { + object NAryType extends TreeExtractor { + val trees: Types.this.type = Types.this + import trees._ + + type SubTree = Type + def unapply(t: Type): Option[(Seq[Type], Seq[Type] => Type)] = t match { case ClassType(ccd, ts) => Some((ts, ts => ClassType(ccd, ts))) case TupleType(ts) => Some((ts, TupleType)) diff --git a/src/main/scala/inox/trees/package.scala b/src/main/scala/inox/trees/package.scala index 669bc61a1..db1812595 100644 --- a/src/main/scala/inox/trees/package.scala +++ b/src/main/scala/inox/trees/package.scala @@ -1,6 +1,6 @@ /* Copyright 2009-2016 EPFL, Lausanne */ -package leon +package inox /** Provides AST definitions for Leon programs. * @@ -19,6 +19,6 @@ package leon * a [[leon.purescala.ScalaPrinter]] that outputs a valid Scala program from a Leon * representation. */ -package object purescala { +package object trees { } -- GitLab