diff --git a/ParseMe.scala b/ParseMe.scala index c6323a6b7434f83d127e4e5786e72795e395808e..ae05f6291bced543d099d314333a4bbb465a8bba 100644 --- a/ParseMe.scala +++ b/ParseMe.scala @@ -2,19 +2,23 @@ import scala.collection.immutable.Set object ParseMe { sealed abstract class Tree - //case class Node(value: Int) extends Tree case class Node(left: Tree, value: Int, right: Tree) extends Tree case class Leaf() extends Tree - def getSet(s: Int): Tree = { - if (4 + s < 2 + s && s > 4) { - emptySet() - } else { - Node(Leaf(), 1, Leaf()) - } + def inside(n: Node): Int = getIt().value + + def getIt() : Node = Node(Leaf(), 12, Leaf()) + + def insert(v: Int, t: Tree): Tree = t match { + case Leaf() if v == 5 => Leaf() + case Leaf() => Node(Leaf(), v, Leaf()) + case Node(Leaf(), 12, Leaf()) => Leaf() + case _ => Leaf() } def emptySet() : Tree = { Leaf() } + + def withIf(a: Int) : Int = { if(a < 0) -a else if(a > 0) a+1 else a-1 } ensuring (_ > 17) } diff --git a/src/funcheck/CodeExtraction.scala b/src/funcheck/CodeExtraction.scala index c912c627447d975d5aecb28893932431ee74bdc1..31e711df0b81884013014fc2bfd141719bb20edd 100644 --- a/src/funcheck/CodeExtraction.scala +++ b/src/funcheck/CodeExtraction.scala @@ -18,8 +18,8 @@ trait CodeExtraction extends Extractors { private lazy val setTraitSym = definitions.getClass("scala.collection.immutable.Set") - private val varSubsts: scala.collection.mutable.Map[Identifier,Function0[Expr]] = - scala.collection.mutable.Map.empty[Identifier,Function0[Expr]] + private val varSubsts: scala.collection.mutable.Map[Symbol,Function0[Expr]] = + scala.collection.mutable.Map.empty[Symbol,Function0[Expr]] private val classesToClasses: scala.collection.mutable.Map[Symbol,ClassTypeDef] = scala.collection.mutable.Map.empty[Symbol,ClassTypeDef] private val defsToDefs: scala.collection.mutable.Map[Symbol,FunDef] = @@ -64,7 +64,7 @@ trait CodeExtraction extends Extractors { top.get } - def extractObjectDef(name: Identifier, tmpl: Template): ObjectDef = { + def extractObjectDef(nameStr: String, tmpl: Template): ObjectDef = { // we assume that the template actually corresponds to an object // definition. Typically it should have been obtained from the proper // extractor (ExObjectDef) @@ -75,10 +75,10 @@ trait CodeExtraction extends Extractors { val scalaClassSyms: scala.collection.mutable.Map[Symbol,Identifier] = scala.collection.mutable.Map.empty[Symbol,Identifier] - val scalaClassArgs: scala.collection.mutable.Map[Symbol,Seq[(Identifier,Tree)]] = - scala.collection.mutable.Map.empty[Symbol,Seq[(Identifier,Tree)]] - val scalaClassNames: scala.collection.mutable.Set[Identifier] = - scala.collection.mutable.Set.empty[Identifier] + val scalaClassArgs: scala.collection.mutable.Map[Symbol,Seq[(String,Tree)]] = + scala.collection.mutable.Map.empty[Symbol,Seq[(String,Tree)]] + val scalaClassNames: scala.collection.mutable.Set[String] = + scala.collection.mutable.Set.empty[String] // we need the new type definitions before we can do anything... tmpl.body.foreach(t => @@ -87,7 +87,7 @@ trait CodeExtraction extends Extractors { if(scalaClassNames.contains(o2)) { unit.error(t.pos, "A class with the same name already exists.") } else { - scalaClassSyms += (sym -> o2) + scalaClassSyms += (sym -> FreshIdentifier(o2)) scalaClassNames += o2 } } @@ -95,7 +95,7 @@ trait CodeExtraction extends Extractors { if(scalaClassNames.contains(o2)) { unit.error(t.pos, "A class with the same name already exists.") } else { - scalaClassSyms += (sym -> o2) + scalaClassSyms += (sym -> FreshIdentifier(o2)) scalaClassNames += o2 scalaClassArgs += (sym -> args) } @@ -138,7 +138,10 @@ trait CodeExtraction extends Extractors { if(p._2.isInstanceOf[CaseClassDef]) { // this should never fail val ccargs = scalaClassArgs(p._1) - p._2.asInstanceOf[CaseClassDef].fields = ccargs.map(cca => VarDecl(cca._1, st2ps(cca._2.tpe))) + p._2.asInstanceOf[CaseClassDef].fields = ccargs.map(cca => { + val cctpe = st2ps(cca._2.tpe) + VarDecl(FreshIdentifier(cca._1).setType(cctpe), cctpe) + }) } }) @@ -185,13 +188,20 @@ trait CodeExtraction extends Extractors { } ) + val name: Identifier = FreshIdentifier(nameStr) val theDef = new ObjectDef(name, objectDefs.reverse ::: classDefs ::: funDefs, Nil) theDef } - def extractFunSig(name: Identifier, params: Seq[ValDef], tpt: Tree): FunDef = { - new FunDef(name, st2ps(tpt.tpe), params.map(p => VarDecl(p.name.toString, st2ps(p.tpt.tpe)))) + def extractFunSig(nameStr: String, params: Seq[ValDef], tpt: Tree): FunDef = { + val newParams = params.map(p => { + val ptpe = st2ps(p.tpt.tpe) + val newID = FreshIdentifier(p.name.toString).setType(ptpe) + varSubsts(p.symbol) = (() => Variable(newID)) + VarDecl(newID, ptpe) + }) + new FunDef(FreshIdentifier(nameStr), st2ps(tpt.tpe), newParams) } def extractFunDef(funDef: FunDef, body: Tree): FunDef = { @@ -200,10 +210,10 @@ trait CodeExtraction extends Extractors { var ensCont: Option[Expr] = None realBody match { - case ExEnsuredExpression(body2, resId, contract) => { - varSubsts(resId) = (() => ResultVariable()) + case ExEnsuredExpression(body2, resSym, contract) => { + varSubsts(resSym) = (() => ResultVariable()) val c1 = s2ps(contract) - varSubsts.remove(resId) + // varSubsts.remove(resSym) realBody = body2 ensCont = Some(c1) } @@ -230,8 +240,8 @@ trait CodeExtraction extends Extractors { stopIfErrors val programName: Identifier = unit.body match { - case PackageDef(name, _) => name.toString - case _ => "<program>" + case PackageDef(name, _) => FreshIdentifier(name.toString) + case _ => FreshIdentifier("<program>") } //Program(programName, ObjectDef("Object", Nil, Nil)) @@ -262,12 +272,24 @@ trait CodeExtraction extends Extractors { * if impossible. If not in 'silent mode', non-pure AST nodes are reported as * errors. */ private def scala2PureScala(unit: CompilationUnit, silent: Boolean)(tree: Tree): Expr = { + def rewriteCaseDef(cd: CaseDef): MatchCase = { + def pat2pat(p: Tree): Pattern = { + WildcardPattern(None) + } + + if(cd.guard == EmptyTree) { + SimpleCase(pat2pat(cd.pat), rec(cd.body)) + } else { + GuardedCase(pat2pat(cd.pat), rec(cd.guard), rec(cd.body)) + } + } + def rec(tr: Tree): Expr = tr match { case ExInt32Literal(v) => IntLiteral(v) case ExBooleanLiteral(v) => BooleanLiteral(v) - case ExIdentifier(id,tpt) => varSubsts.get(id) match { + case ExIdentifier(sym,tpt) => varSubsts.get(sym) match { case Some(fun) => fun() - case None => Variable(id) + case None => Variable(FreshIdentifier(sym.name.toString)) // TODO this is SO wrong } case ExCaseClassConstruction(tpt, args) => { val cctype = scalaType2PureScala(unit, silent)(tpt.tpe) @@ -278,27 +300,68 @@ trait CodeExtraction extends Extractors { throw ImpureCodeEncounteredException(tree) } val nargs = args.map(rec(_)) - CaseClass(cctype.asInstanceOf[CaseClassType], nargs) + val cct = cctype.asInstanceOf[CaseClassType] + CaseClass(cct.classDef, nargs).setType(cct) + } + case ExAnd(l, r) => And(rec(l), rec(r)).setType(BooleanType) + case ExOr(l, r) => Or(rec(l), rec(r)).setType(BooleanType) + case ExNot(e) => Not(rec(e)).setType(BooleanType) + case ExUMinus(e) => UMinus(rec(e)).setType(Int32Type) + case ExPlus(l, r) => Plus(rec(l), rec(r)).setType(Int32Type) + case ExMinus(l, r) => Minus(rec(l), rec(r)).setType(Int32Type) + case ExTimes(l, r) => Times(rec(l), rec(r)).setType(Int32Type) + case ExDiv(l, r) => Division(rec(l), rec(r)).setType(Int32Type) + case ExEquals(l, r) => Equals(rec(l), rec(r)).setType(BooleanType) + case ExGreaterThan(l, r) => GreaterThan(rec(l), rec(r)).setType(BooleanType) + case ExGreaterEqThan(l, r) => GreaterEquals(rec(l), rec(r)).setType(BooleanType) + case ExLessThan(l, r) => LessThan(rec(l), rec(r)).setType(BooleanType) + case ExLessEqThan(l, r) => LessEquals(rec(l), rec(r)).setType(BooleanType) + case ExIfThenElse(t1,t2,t3) => { + val r1 = rec(t1) + val r2 = rec(t2) + val r3 = rec(t3) + IfExpr(r1, r2, r3).setType(leastUpperBound(r2.getType, r3.getType)) } - case ExAnd(l, r) => And(rec(l), rec(r)) - case ExOr(l, r) => Or(rec(l), rec(r)) - case ExNot(e) => Not(rec(e)) - case ExUMinus(e) => UMinus(rec(e)) - case ExPlus(l, r) => Plus(rec(l), rec(r)) - case ExMinus(l, r) => Minus(rec(l), rec(r)) - case ExTimes(l, r) => Times(rec(l), rec(r)) - case ExDiv(l, r) => Division(rec(l), rec(r)) - case ExEquals(l, r) => Equals(rec(l), rec(r)) - case ExGreaterThan(l, r) => GreaterThan(rec(l), rec(r)) - case ExGreaterEqThan(l, r) => GreaterEquals(rec(l), rec(r)) - case ExLessThan(l, r) => LessThan(rec(l), rec(r)) - case ExLessEqThan(l, r) => LessEquals(rec(l), rec(r)) - case ExIfThenElse(t1,t2,t3) => IfExpr(rec(t1), rec(t2), rec(t3)) case ExLocalCall(sy,nm,ar) => { if(defsToDefs.keysIterator.find(_ == sy).isEmpty) { - unit.error(tr.pos, "Invoking an invalid function.") + if(!silent) + unit.error(tr.pos, "Invoking an invalid function.") + throw ImpureCodeEncounteredException(tr) + } + val fd = defsToDefs(sy) + FunctionInvocation(fd, ar.map(rec(_))).setType(fd.returnType) + } + case ExPatternMatching(sel, cses) => { + val rs = rec(sel) + val rc = cses.map(rewriteCaseDef(_)) + val rt: purescala.TypeTrees.TypeTree = rc.map(_.rhs.getType).reduceLeft(leastUpperBound(_,_)) + MatchExpr(rs, rc).setType(rt) + } + + // this one should stay after all others, cause it also catches UMinus + // and Not, for instance. + case ExParameterlessMethodCall(t,n) => { + val selector = rec(t) + val selType = selector.getType + + if(!selType.isInstanceOf[CaseClassType]) { + if(!silent) + unit.error(tr.pos, "Invalid method or field invocation (not purescala?)") + throw ImpureCodeEncounteredException(tr) } - FunctionInvocation(defsToDefs(sy), ar.map(rec(_))) + + val selDef: CaseClassDef = selType.asInstanceOf[CaseClassType].classDef + + val fieldID = selDef.fields.find(_.id.name == n.toString) match { + case None => { + if(!silent) + unit.error(tr.pos, "Invalid method or field invocation (not a case class arg?)") + throw ImpureCodeEncounteredException(tr) + } + case Some(vd) => vd.id + } + + CaseClassSelector(selector, fieldID).setType(fieldID.getType) } // default behaviour is to complain :) @@ -319,10 +382,7 @@ trait CodeExtraction extends Extractors { case tpe if tpe == IntClass.tpe => Int32Type case tpe if tpe == BooleanClass.tpe => BooleanType case TypeRef(_, sym, btt :: Nil) if sym == setTraitSym => SetType(rec(btt)) - case TypeRef(_, sym, Nil) if classesToClasses.keySet.contains(sym) => classesToClasses(sym) match { - case a: AbstractClassDef => AbstractClassType(a) - case c: CaseClassDef => CaseClassType(c) - } + case TypeRef(_, sym, Nil) if classesToClasses.keySet.contains(sym) => classDefToClassType(classesToClasses(sym)) case _ => { if(!silent) { diff --git a/src/funcheck/Extractors.scala b/src/funcheck/Extractors.scala index d4acfe254e98fe2e45309a28331b4b539e0cc25a..7fba429eb44c65022a17fd0ffcd4fc97e89c7d3b 100644 --- a/src/funcheck/Extractors.scala +++ b/src/funcheck/Extractors.scala @@ -23,7 +23,7 @@ trait Extractors { object ExEnsuredExpression { /** Extracts the 'ensuring' contract from an expression. */ - def unapply(tree: Apply): Option[(Tree,String,Tree)] = tree match { + def unapply(tree: Apply): Option[(Tree,Symbol,Tree)] = tree match { case Apply( Select( Apply( @@ -32,9 +32,8 @@ trait Extractors { TypeTree() :: Nil), body :: Nil), ensuringName), - (anonymousFun @ Function(ValDef(_, resultName, resultType, EmptyTree) :: Nil, - contractBody)) :: Nil) - if("ensuring".equals(ensuringName.toString)) => Some((body, resultName.toString, contractBody)) + (Function((vd @ ValDef(_, _, _, EmptyTree)) :: Nil, contractBody)) :: Nil) + if("ensuring".equals(ensuringName.toString)) => Some((body, vd.symbol, contractBody)) case _ => None } } @@ -155,8 +154,8 @@ trait Extractors { } object ExIdentifier { - def unapply(tree: Ident): Option[(String,Tree)] = tree match { - case i: Ident => Some((i.symbol.name.toString, i)) + def unapply(tree: Ident): Option[(Symbol,Tree)] = tree match { + case i: Ident => Some((i.symbol, i)) case _ => None } } @@ -274,5 +273,18 @@ trait Extractors { case _ => None } } + + // used for case classes selectors. + object ExParameterlessMethodCall { + def unapply(tree: Select): Option[(Tree,Name)] = tree match { + case Select(lhs, n) => Some((lhs, n)) + case _ => None + } + } + + object ExPatternMatching { + def unapply(tree: Match): Option[(Tree,List[CaseDef])] = + if(tree != null) Some((tree.selector, tree.cases)) else None + } } } diff --git a/src/funcheck/FunCheckPlugin.scala b/src/funcheck/FunCheckPlugin.scala index 9c1ad9a6df5c7f16210d02e3dd813f0d2e79a7fc..9026ed4d24eb0b020f63f2ad9890ffe7451f041b 100644 --- a/src/funcheck/FunCheckPlugin.scala +++ b/src/funcheck/FunCheckPlugin.scala @@ -15,13 +15,17 @@ class FunCheckPlugin(val global: Global) extends Plugin { /** The help message displaying the options for that plugin. */ override val optionsHelp: Option[String] = Some( - " -P:funcheck:with-code Allows the compiler to keep running after the static analysis") + " -P:funcheck:uniqid When pretty-printing funcheck trees, show identifiers IDs" + + "\n" + + " -P:funcheck:with-code Allows the compiler to keep running after the static analysis" + ) /** Processes the command-line options. */ override def processOptions(options: List[String], error: String => Unit) { for(option <- options) { option match { case "with-code" => stopAfterAnalysis = false + case "uniqid" => purescala.Settings.showIDs = true case _ => error("Invalid option: " + option) } } diff --git a/src/purescala/Common.scala b/src/purescala/Common.scala index cfbe10b3a158cd49301e058a11cfdc5d4894d772..ddb2129e10913f3b875af0a5d8c85d7b7ce4b1a6 100644 --- a/src/purescala/Common.scala +++ b/src/purescala/Common.scala @@ -1,5 +1,39 @@ package purescala object Common { - type Identifier = String + import TypeTrees.Typed + + // the type is left blank (NoType) for Identifiers that are not variables + class Identifier private[Common](val name: String, val id: Int) extends Typed { + override def equals(other: Any): Boolean = { + if(other == null || !other.isInstanceOf[Identifier]) + false + else + other.asInstanceOf[Identifier].id == this.id + } + + override def hashCode: Int = id + + override def toString: String = { + if(purescala.Settings.showIDs) { + // angle brackets: name + "\u3008" + id + "\u3009" + name + "[" + id + "]" + } else { + name + } + } + } + + private object UniqueCounter { + private var soFar: Int = -1 + + def next: Int = { + soFar = soFar + 1 + soFar + } + } + + object FreshIdentifier { + def apply(name: String) : Identifier = new Identifier(name, UniqueCounter.next) + } } diff --git a/src/purescala/Definitions.scala b/src/purescala/Definitions.scala index cae4ba43106c57a04dd48533c8ef897d4d5cf086..837478e2500e98179687976c7eb694e833f194b6 100644 --- a/src/purescala/Definitions.scala +++ b/src/purescala/Definitions.scala @@ -5,50 +5,13 @@ object Definitions { import Trees._ import TypeTrees._ - private object UniqueID { - import scala.collection.mutable.HashMap - - private val counts: HashMap[String,Int] = new HashMap[String,Int] - - def apply(prefix: String): Int = { - val count = counts.getOrElse(prefix, 0) - counts(prefix) = count + 1 - count - } - } - - /** Scopes add new definitions to the surrounding scope. */ - // trait Scope { - // import scala.collection.immutable.Map - - // def parentScope: Option[Scope] = None - // def lookupVar(id: Identifier): Option[VarDecl] = None - // def lookupObject(id: Identifier): Option[ObjectDef] = None - // def lookupClassType(id: Identifier): Option[ClassTypeDef] = None - // def lookupAbstractClass(id: Identifier): Option[AbstractClassDef] = None - // def lookupCaseClass(id: Identifier): Option[CaseClassDef] = None - // def lookupClass(id: Identifier): Option[ClassDef] = None - // def lookupFun(id: Identifier): Option[FunDef] = None - // } - - /** A declaration is anything "structural", ie. anything that's part of the - * syntax of a program but not in an expression. */ - sealed abstract class Definition /*extends Scope*/ { + sealed abstract class Definition { val id: Identifier override def toString: String = PrettyPrinter(this) } /** A VarDecl declares a new identifier to be of a certain type. */ case class VarDecl(id: Identifier, tpe: TypeTree) extends Typed { - val uniqueID: Int = UniqueID(id.toString) - - override val toString: String = id + uniqueID - - override def equals(other: Any) = other match { - case v: VarDecl => this.equals(v) && uniqueID == v.uniqueID - case _ => false - } - override def getType = tpe override def setType(tt: TypeTree) = scala.Predef.error("Can't set type of VarDecl.") } @@ -57,17 +20,7 @@ object Definitions { /** A wrapper for a program. For now a program is simply a single object. The * name is meaningless and we just use the package name as id. */ - case class Program(id: Identifier, mainObject: ObjectDef) extends Definition { - //override val parentScope = None - - // override def lookupObject(id: Identifier) = { - // if(id == mainObject.id) { - // Some(mainObject) - // } else { - // None - // } - // } - } + case class Program(id: Identifier, mainObject: ObjectDef) extends Definition /** Objects work as containers for class definitions, functions (def's) and * val's. */ diff --git a/src/purescala/PrettyPrinter.scala b/src/purescala/PrettyPrinter.scala index 574ae4423c9312afb765a51f115359ddda61f8e9..8f6a5b9562e6c453912f38acb51ce6080cd9baac 100644 --- a/src/purescala/PrettyPrinter.scala +++ b/src/purescala/PrettyPrinter.scala @@ -12,12 +12,12 @@ object PrettyPrinter { import java.lang.StringBuffer def apply(tree: Expr): String = { - val retSB = pp(tree, new StringBuffer) + val retSB = pp(tree, new StringBuffer, 0) retSB.toString } def apply(tpe: TypeTree): String = { - val retSB = pp(tpe, new StringBuffer) + val retSB = pp(tpe, new StringBuffer, 0) retSB.toString } @@ -26,82 +26,125 @@ object PrettyPrinter { retSB.toString } + private def ind(sb: StringBuffer, lvl: Int) : StringBuffer = { + sb.append(" " * lvl) + sb + } + // EXPRESSIONS // all expressions are printed in-line - private def ppUnary(sb: StringBuffer, expr: Expr, op: String): StringBuffer = { + private def ppUnary(sb: StringBuffer, expr: Expr, op: String, lvl: Int): StringBuffer = { var nsb: StringBuffer = sb nsb.append(op) nsb.append("(") - nsb = pp(expr, nsb) + nsb = pp(expr, nsb, lvl) nsb.append(")") nsb } - private def ppBinary(sb: StringBuffer, left: Expr, right: Expr, op: String): StringBuffer = { + private def ppBinary(sb: StringBuffer, left: Expr, right: Expr, op: String, lvl: Int): StringBuffer = { var nsb: StringBuffer = sb nsb.append("(") - nsb = pp(left, nsb) + nsb = pp(left, nsb, lvl) nsb.append(op) - nsb = pp(right, nsb) + nsb = pp(right, nsb, lvl) nsb.append(")") nsb } - private def ppNary(sb: StringBuffer, exprs: Seq[Expr], op: String): StringBuffer = { + private def ppNary(sb: StringBuffer, exprs: Seq[Expr], op: String, lvl: Int): StringBuffer = { var nsb = sb nsb.append("(") val sz = exprs.size var c = 0 exprs.foreach(ex => { - nsb = pp(ex, nsb) ; c += 1 ; if(c < sz) nsb.append(op) + nsb = pp(ex, nsb, lvl) ; c += 1 ; if(c < sz) nsb.append(op) }) nsb.append(")") nsb } - private def pp(tree: Expr, sb: StringBuffer): StringBuffer = tree match { + private def pp(tree: Expr, sb: StringBuffer, lvl: Int): StringBuffer = tree match { case Variable(id) => sb.append(id) - case And(exprs) => ppNary(sb, exprs, " \u2227 ") // \land - case Or(exprs) => ppNary(sb, exprs, " \u2228 ") // \lor - case Not(Equals(l, r)) => ppBinary(sb, l, r, " \u2260 ") // \neq - case Not(expr) => ppUnary(sb, expr, "\u00AC") // \neg - case UMinus(expr) => ppUnary(sb, expr, "-") - case Equals(l,r) => ppBinary(sb, l, r, " == ") + case And(exprs) => ppNary(sb, exprs, " \u2227 ", lvl) // \land + case Or(exprs) => ppNary(sb, exprs, " \u2228 ", lvl) // \lor + case Not(Equals(l, r)) => ppBinary(sb, l, r, " \u2260 ", lvl) // \neq + case Not(expr) => ppUnary(sb, expr, "\u00AC", lvl) // \neg + case UMinus(expr) => ppUnary(sb, expr, "-", lvl) + case Equals(l,r) => ppBinary(sb, l, r, " == ", lvl) case IntLiteral(v) => sb.append(v) case BooleanLiteral(v) => sb.append(v) case StringLiteral(s) => sb.append("\"" + s + "\"") case CaseClass(ct, args) => { var nsb = sb nsb.append(ct.id) - nsb = ppNary(nsb, args, ", ") + nsb = ppNary(nsb, args, ", ", lvl) nsb } + case CaseClassSelector(cc, id) => pp(cc, sb, lvl).append("." + id) case FunctionInvocation(fd, args) => { var nsb = sb nsb.append(fd.id) - nsb = ppNary(nsb, args, ", ") + nsb = ppNary(nsb, args, ", ", lvl) nsb } - case Plus(l,r) => ppBinary(sb, l, r, " + ") - case Minus(l,r) => ppBinary(sb, l, r, " - ") - case Times(l,r) => ppBinary(sb, l, r, " * ") - case Division(l,r) => ppBinary(sb, l, r, " / ") - case LessThan(l,r) => ppBinary(sb, l, r, " < ") - case GreaterThan(l,r) => ppBinary(sb, l, r, " > ") - case LessEquals(l,r) => ppBinary(sb, l, r, " \u2264 ") // \leq - case GreaterEquals(l,r) => ppBinary(sb, l, r, " \u2265 ") // \geq + case Plus(l,r) => ppBinary(sb, l, r, " + ", lvl) + case Minus(l,r) => ppBinary(sb, l, r, " - ", lvl) + case Times(l,r) => ppBinary(sb, l, r, " * ", lvl) + case Division(l,r) => ppBinary(sb, l, r, " / ", lvl) + case LessThan(l,r) => ppBinary(sb, l, r, " < ", lvl) + case GreaterThan(l,r) => ppBinary(sb, l, r, " > ", lvl) + case LessEquals(l,r) => ppBinary(sb, l, r, " \u2264 ", lvl) // \leq + case GreaterEquals(l,r) => ppBinary(sb, l, r, " \u2265 ", lvl) // \geq case IfExpr(c, t, e) => { var nsb = sb nsb.append("if (") - nsb = pp(c, nsb) - nsb.append(") { ") - nsb.append(t) - nsb.append(" } else { ") - nsb.append(e) - nsb.append(" }") + nsb = pp(c, nsb, lvl) + nsb.append(") {\n") + ind(nsb, lvl+1) + nsb = pp(t, nsb, lvl+1) + nsb.append("\n") + ind(nsb, lvl) + nsb.append("} else {\n") + ind(nsb, lvl+1) + nsb = pp(e, nsb, lvl+1) + nsb.append("\n") + ind(nsb, lvl) + nsb.append("}") + nsb + } + + case MatchExpr(s, csc) => { + def ppc(sb: StringBuffer, p: Pattern): StringBuffer = p match { + //case InstanceOfPattern(None, ctd) => + //case InstanceOfPattern(Some(id), ctd) => + //case CaseClassPattern(None, ccd, subps) => + //case CaseClassPattern(Some(id), ccd, subps) => + case WildcardPattern(None) => sb.append("_") + case WildcardPattern(Some(id)) => sb.append(id) + case _ => sb.append("Pattern?") + } + + var nsb = sb + nsb == pp(s, nsb, lvl) + nsb.append(" match {\n") + + csc.foreach(cs => { + ind(nsb, lvl+1) + nsb.append("case ") + nsb = ppc(nsb, cs.pattern) + cs.theGuard.foreach(g => { + nsb.append(" if ") + nsb = pp(g, nsb, lvl+1) + }) + nsb.append(" => ") + nsb = pp(cs.rhs, nsb, lvl+1) + nsb.append("\n") + }) + ind(nsb, lvl).append("}") nsb } @@ -112,10 +155,11 @@ object PrettyPrinter { // TYPE TREES // all type trees are printed in-line - private def pp(tpe: TypeTree, sb: StringBuffer): StringBuffer = tpe match { + private def pp(tpe: TypeTree, sb: StringBuffer, lvl: Int): StringBuffer = tpe match { + case NoType => sb.append("???") case Int32Type => sb.append("Int") case BooleanType => sb.append("Boolean") - case SetType(bt) => pp(bt, sb.append("Set[")).append("]") + case SetType(bt) => pp(bt, sb.append("Set["), lvl).append("]") case c: ClassType => sb.append(c.classDef.id) case _ => sb.append("Type?") } @@ -123,9 +167,6 @@ object PrettyPrinter { // DEFINITIONS // all definitions are printed with an end-of-line private def pp(defn: Definition, sb: StringBuffer, lvl: Int): StringBuffer = { - def ind(sb: StringBuffer, customLevel: Int = lvl) : Unit = { - sb.append(" " * customLevel) - } defn match { case Program(id, mainObj) => { @@ -138,7 +179,7 @@ object PrettyPrinter { case ObjectDef(id, defs, invs) => { var nsb = sb - ind(nsb) + ind(nsb, lvl) nsb.append("object ") nsb.append(id) nsb.append(" {\n") @@ -149,17 +190,18 @@ object PrettyPrinter { defs.foreach(df => { nsb = pp(df, nsb, lvl+1) if(c < sz - 1) { - nsb.append("\n") + nsb.append("\n\n") } c = c + 1 }) - ind(nsb); nsb.append("}\n") + nsb.append("\n") + ind(nsb, lvl).append("}\n") } case AbstractClassDef(id, parent) => { var nsb = sb - ind(nsb) + ind(nsb, lvl) nsb.append("sealed abstract class ") nsb.append(id) parent.foreach(p => nsb.append(" extends " + p.id)) @@ -168,7 +210,7 @@ object PrettyPrinter { case CaseClassDef(id, parent, varDecls) => { var nsb = sb - ind(nsb) + ind(nsb, lvl) nsb.append("case class ") nsb.append(id) nsb.append("(") @@ -176,9 +218,9 @@ object PrettyPrinter { val sz = varDecls.size varDecls.foreach(vd => { - nsb.append(vd.id.toString) + nsb.append(vd.id) nsb.append(": ") - nsb = pp(vd.tpe, nsb) + nsb = pp(vd.tpe, nsb, lvl) if(c < sz - 1) { nsb.append(", ") } @@ -193,20 +235,20 @@ object PrettyPrinter { var nsb = sb pre.foreach(prec => { - ind(nsb) + ind(nsb, lvl) nsb.append("@pre : ") - nsb = pp(prec, nsb) + nsb = pp(prec, nsb, lvl) nsb.append("\n") }) post.foreach(postc => { - ind(nsb) + ind(nsb, lvl) nsb.append("@post: ") - nsb = pp(postc, nsb) + nsb = pp(postc, nsb, lvl) nsb.append("\n") }) - ind(nsb) + ind(nsb, lvl) nsb.append("def ") nsb.append(id) nsb.append("(") @@ -217,7 +259,7 @@ object PrettyPrinter { args.foreach(arg => { nsb.append(arg.id) nsb.append(" : ") - nsb = pp(arg.tpe, nsb) + nsb = pp(arg.tpe, nsb, lvl) if(c < sz - 1) { nsb.append(", ") @@ -226,15 +268,9 @@ object PrettyPrinter { }) nsb.append(") : ") - nsb = pp(rt, nsb) - nsb.append(" = {\n") - - ind(nsb, lvl+1) - nsb = pp(body, nsb) - nsb.append("\n") - - ind(nsb) - nsb.append("}\n") + nsb = pp(rt, nsb, lvl) + nsb.append(" = ") + pp(body, nsb, lvl) } case _ => sb.append("Defn?") diff --git a/src/purescala/Trees.scala b/src/purescala/Trees.scala index b89a6ac1ef3d6cfe33ae454a5a6463601beb6862..17883d57b8e43da5c970f61f148ec2eb51440ec3 100644 --- a/src/purescala/Trees.scala +++ b/src/purescala/Trees.scala @@ -20,17 +20,23 @@ object Trees { sealed abstract class MatchCase { val pattern: Pattern val rhs: Expr + val theGuard: Option[Expr] } - case class SimpleCase(pattern: Pattern, rhs: Expr) extends MatchCase - case class GuardedCase(pattern: Pattern, guard: Expr, rhs: Expr) extends MatchCase + case class SimpleCase(pattern: Pattern, rhs: Expr) extends MatchCase { + val theGuard = None + } + case class GuardedCase(pattern: Pattern, guard: Expr, rhs: Expr) extends MatchCase { + val theGuard = Some(guard) + } sealed abstract class Pattern case class InstanceOfPattern(binder: Option[Identifier], classTypeDef: ClassTypeDef) extends Pattern // c: Class case class WildcardPattern(binder: Option[Identifier]) extends Pattern // c @ _ - case class ExtractorPattern(binder: Option[Identifier], - extractor : ExtractorTypeDef, - subPatterns: Seq[Pattern]) extends Pattern // c @ Extractor(...,...) + case class CaseClassPattern(binder: Option[Identifier], caseClassDef: CaseClassDef, subPatterns: Seq[Pattern]) extends Pattern + // case class ExtractorPattern(binder: Option[Identifier], + // extractor : ExtractorTypeDef, + // subPatterns: Seq[Pattern]) extends Pattern // c @ Extractor(...,...) // We don't handle Seq stars for now. /* Propositional logic */ @@ -60,9 +66,10 @@ object Trees { case class Equals(left: Expr, right: Expr) extends Expr /* Literals */ - // to be fixed! Should contain a reference to the definition of that - // variable, which would also give us its type. - case class Variable(id: Identifier) extends Expr + case class Variable(id: Identifier) extends Expr { + override def getType = id.getType + override def setType(tt: TypeTree) = { id.setType(tt); this } + } // represents the result in post-conditions case class ResultVariable() extends Expr @@ -75,7 +82,8 @@ object Trees { case class BooleanLiteral(value: Boolean) extends Literal[Boolean] case class StringLiteral(value: String) extends Literal[String] - case class CaseClass(classType: CaseClassType, args: Seq[Expr]) extends Expr + case class CaseClass(classDef: CaseClassDef, args: Seq[Expr]) extends Expr + case class CaseClassSelector(caseClass: Expr, selector: Identifier) extends Expr /* Arithmetic */ case class Plus(lhs: Expr, rhs: Expr) extends Expr diff --git a/src/purescala/TypeTrees.scala b/src/purescala/TypeTrees.scala index 35740fbaa08455b0ea7a5fd1faf4a13179bc48fd..1e7c115bdec57567f6a0c0f9ff660216b4e363f3 100644 --- a/src/purescala/TypeTrees.scala +++ b/src/purescala/TypeTrees.scala @@ -25,6 +25,41 @@ object TypeTrees { override def toString: String = PrettyPrinter(this) } + def leastUpperBound(t1: TypeTree, t2: TypeTree): TypeTree = (t1,t2) match { + case (c1: ClassType, c2: ClassType) => { + import scala.collection.immutable.Set + var c: ClassTypeDef = c1.classDef + var visited: Set[ClassTypeDef] = Set(c) + + while(c.parent.isDefined) { + c = c.parent.get + visited = visited ++ Set(c) + } + + c = c2.classDef + var found: Option[ClassTypeDef] = if(visited.contains(c)) { + Some(c) + } else { + None + } + + while(found.isEmpty && c.parent.isDefined) { + c = c.parent.get + if(visited.contains(c)) + found = Some(c) + } + + if(found.isEmpty) { + scala.Predef.error("Asking for lub of unrelated class types : " + t1 + " and " + t2) + } else { + classDefToClassType(found.get) + } + } + + case (o1, o2) if (o1 == o2) => o1 + case _ => scala.Predef.error("Asking for lub of unrelated types: " + t1 + " and " + t2) + } + case object NoType extends TypeTree case object AnyType extends TypeTree @@ -37,13 +72,17 @@ object TypeTrees { case class SetType(base: TypeTree) extends TypeTree // case class MultisetType(base: TypeTree) extends TypeTree case class MapType(from: TypeTree, to: TypeTree) extends TypeTree + case class OptionType(base: TypeTree) extends TypeTree sealed abstract class ClassType extends TypeTree { val classDef: ClassTypeDef val id: Identifier = classDef.id } - case class AbstractClassType(classDef: AbstractClassDef) extends ClassType - case class CaseClassType(classDef: CaseClassDef) extends ClassType - case class OptionType(base: TypeTree) extends TypeTree + case class CaseClassType(classDef: CaseClassDef) extends ClassType + + def classDefToClassType(cd: ClassTypeDef): ClassType = cd match { + case a: AbstractClassDef => AbstractClassType(a) + case c: CaseClassDef => CaseClassType(c) + } }