diff options
3 files changed, 163 insertions, 84 deletions
diff --git a/src/compiler/scala/tools/nsc/matching/MatchUtil.scala b/src/compiler/scala/tools/nsc/matching/MatchUtil.scala index cbbb736d2d..e6cf468328 100644 --- a/src/compiler/scala/tools/nsc/matching/MatchUtil.scala +++ b/src/compiler/scala/tools/nsc/matching/MatchUtil.scala @@ -13,62 +13,22 @@ object MatchUtil def impossible: Nothing = abort("this never happens") def abort(msg: String): Nothing = throw new RuntimeException(msg) - object Implicits { - implicit def listPlusOps[T](xs: List[T]) = new ListPlus(xs) - } - - class ListPlus[A](list: List[A]) { - /** Returns the list without the element at index <code>n</code>. - * If this list has fewer than <code>n</code> elements, the same list is returned. - * - * @param n the index of the element to drop. - * @return the list without the <code>n</code>th element. - */ - def dropIndex(n: Int) = list.take(n) ::: list.drop(n + 1) - - /** Returns a list formed from this list and the specified lists <code>list2</code> - * and <code>list3</code> by associating each element of the first list with - * the elements at the same positions in the other two. - * If any of the lists is shorter than the others, later elements in the other two are ignored. - * - * @return <code>List((a<sub>0</sub>,b<sub>0</sub>), ..., - * (a<sub>min(m,n)</sub>,b<sub>min(m,n)</sub>))</code> when - * <code>List(a<sub>0</sub>, ..., a<sub>m</sub>) - * zip List(b<sub>0</sub>, ..., b<sub>n</sub>)</code> is invoked. - */ - def zip3[B, C](list2: List[B], list3: List[C]): List[(A, B, C)] = { - val b = new ListBuffer[(A, B, C)] - var xs1 = list - var xs2 = list2 - var xs3 = list3 - while (!xs1.isEmpty && !xs2.isEmpty && !xs3.isEmpty) { - b += ((xs1.head, xs2.head, xs3.head)) - xs1 = xs1.tail - xs2 = xs2.tail - xs3 = xs3.tail - } - b.toList - } - } - - object ListPlus { - /** Transforms a list of triples into a triple of lists. - * - * @param xs the list of triples to unzip - * @return a triple of lists. - */ - def unzip3[A,B,C](xs: List[(A,B,C)]): (List[A], List[B], List[C]) = { - val b1 = new ListBuffer[A] - val b2 = new ListBuffer[B] - val b3 = new ListBuffer[C] - var xc = xs - while (!xc.isEmpty) { - b1 += xc.head._1 - b2 += xc.head._2 - b3 += xc.head._3 - xc = xc.tail - } - (b1.toList, b2.toList, b3.toList) + /** Transforms a list of triples into a triple of lists. + * + * @param xs the list of triples to unzip + * @return a triple of lists. + */ + def unzip3[A,B,C](xs: List[(A,B,C)]): (List[A], List[B], List[C]) = { + val b1 = new ListBuffer[A] + val b2 = new ListBuffer[B] + val b3 = new ListBuffer[C] + var xc = xs + while (!xc.isEmpty) { + b1 += xc.head._1 + b2 += xc.head._2 + b3 += xc.head._3 + xc = xc.tail } + (b1.toList, b2.toList, b3.toList) } } diff --git a/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala b/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala index 353b8c4102..44fe1456fa 100644 --- a/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala +++ b/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala @@ -11,8 +11,6 @@ import util.Position import collection._ import mutable.BitSet import immutable.IntMap -import MatchUtil.ListPlus._ -import MatchUtil.Implicits._ import MatchUtil._ /** Translation of match expressions. @@ -49,7 +47,7 @@ trait ParallelMatching extends ast.TreeDSL { def ifDebug(body: => Unit): Unit = { if (settings.debug.value) body } def DBG(msg: String): Unit = { ifDebug(println(msg)) } - def TRACE(f: String, xs: String*): Unit = { if (trace) println(f.format(xs : _*)) } + def TRACE(f: String, xs: Any*): Unit = { if (trace) println(if (xs.isEmpty) f else f.format(xs : _*)) } def logAndReturn[T](s: String, x: T): T = { log(s + x.toString) ; x } def traceAndReturn[T](s: String, x: T): T = { TRACE(s + x.toString) ; x } @@ -808,7 +806,7 @@ trait ParallelMatching extends ast.TreeDSL { ( for ((i, l) <- labels) yield "labels(%d) = %s".format(i, l) ) ++ ( for ((s, v) <- List("bx" -> bx, "label.tpe" -> label.tpe)) yield "%s = %s".format(s, v) ) - xs.flatten mkString "\n" + xs mkString "\n" } // sanity checks: same length lists and args are conformant with formals def isConsistent() = (fmls.length == args.length) && List.forall2(args, fmls)(_.tpe <:< _) @@ -917,6 +915,21 @@ trait ParallelMatching extends ast.TreeDSL { if (row.length != row1.length) make(temp, row) // recursive call if any change else Rep(temp, row).init } + + override def toString() = { + val toPrint: List[(Any, Traversable[Any])] = + ((vss.zipWithIndex map (_.swap)) ::: + List[(Any, Traversable[Any])]( + "labels" -> labels, + "targets" -> targets, + "reached" -> reached, + "shortCuts" -> shortCuts) + ) filterNot (_._2.isEmpty) + val strs = toPrint map { case (k, v) => " %s = %s\n".format(k, v) } + + if (toPrint.isEmpty) "RepFactory()" + else "RepFactory(\n%s)".format(strs mkString) + } } case class Combo(index: Int, sym: Symbol) @@ -976,30 +989,33 @@ trait ParallelMatching extends ast.TreeDSL { * * tmp1 tmp_m */ - final def applyRule(implicit theOwner: Symbol, rep: RepFactory): RuleApplication = row match { - case Nil => ErrorRule() - case Row(pats, subst, g, bx) :: xs => - - var bnd = subst - for (((rpat, t), px) <- pats zip temp zipWithIndex) { - val Strip(vs, p) = rpat - - if (isDefaultPattern(p)) bnd = bnd.add(vs, t) - else { - // Row( _ ... _ p_1i ... p_1n g_m b_m ) :: rows - // cut out column px that contains the non-default pattern - val column = rpat :: (row.tail map (_ pat px)) - val restTemp = temp dropIndex px - val restRows = row map (r => r replace (r.pat dropIndex px)) - val mr = MixtureRule(new Scrutinee(t), column, rep.make(restTemp, restRows)) - - // TRACE("Mixture rule is = " + mr.getClass) - return mr + final def applyRule(implicit theOwner: Symbol, rep: RepFactory): RuleApplication = { + def dropIndex[T](xs: List[T], n: Int) = (xs take n) ::: (xs drop (n + 1)) + row match { + case Nil => ErrorRule() + case Row(pats, subst, g, bx) :: xs => + + var bnd = subst + for (((rpat, t), px) <- pats zip temp zipWithIndex) { + val Strip(vs, p) = rpat + + if (isDefaultPattern(p)) bnd = bnd.add(vs, t) + else { + // Row( _ ... _ p_1i ... p_1n g_m b_m ) :: rows + // cut out column px that contains the non-default pattern + val column = rpat :: (row.tail map (_ pat px)) + val restTemp = dropIndex(temp, px) + val restRows = row map (r => r replace dropIndex(r.pat, px)) + val mr = MixtureRule(new Scrutinee(t), column, rep.make(restTemp, restRows)) + + // TRACE("Mixture rule is = " + mr.getClass) + return mr + } } - } - // Row( _ ... _ g_1 b_1 ) :: rows it's all default patterns - val rest = if (g.isEmpty) null else rep.make(temp, xs) // TODO - why null? - VariableRule (bnd, g, rest, bx) + // Row( _ ... _ g_1 b_1 ) :: rows it's all default patterns + val rest = if (g.isEmpty) null else rep.make(temp, xs) // TODO - why null? + VariableRule (bnd, g, rest, bx) + } } // a fancy toString method for debugging diff --git a/src/compiler/scala/tools/nsc/matching/TransMatcher.scala b/src/compiler/scala/tools/nsc/matching/TransMatcher.scala index a348ea83a4..183b54494e 100644 --- a/src/compiler/scala/tools/nsc/matching/TransMatcher.scala +++ b/src/compiler/scala/tools/nsc/matching/TransMatcher.scala @@ -8,12 +8,15 @@ package scala.tools.nsc.matching import util.Position +import ast.{ TreePrinters, Trees } +import symtab.SymbolTable +import java.io.{ StringWriter, PrintWriter } /** Translation of pattern matching * * @author Burak Emir */ -trait TransMatcher extends ast.TreeDSL { +trait TransMatcher extends ast.TreeDSL with CompactTreePrinter { self: transform.ExplicitOuter with PatternNodes with ParallelMatching with CodeFactory => import global.{ typer => _, _ } @@ -81,15 +84,28 @@ trait TransMatcher extends ast.TreeDSL { val mch = typer typed irep.toTree var dfatree = typer typed Block(vds, mch) + TRACE("handlePattern(\n tmps = %s\n cases = %s\n rep = %s\n initRep = %s\n)", + tmps, cases.mkString("(\n ", "\n ", "\n)"), rep, irep) + TRACE("dfatree(1) = " + toCompactString(dfatree)) + // cannot use squeezedBlock because of side-effects, see t275 for ((cs, bx) <- cases.zipWithIndex) if (!rep.isReached(bx)) cunit.error(cs.body.pos, "unreachable code") dfatree = rep cleanup dfatree resetTraverser traverse dfatree + TRACE("dfatree(2) = " + toCompactString(dfatree)) dfatree } + private def toCompactString(t: Tree): String = { + val buffer = new StringWriter() + val printer = compactTreePrinters.create(new PrintWriter(buffer)) + printer.print(t) + printer.flush() + buffer.toString + } + private object resetTraverser extends Traverser { override def traverse(x: Tree): Unit = x match { case vd: ValDef => @@ -101,3 +117,90 @@ trait TransMatcher extends ast.TreeDSL { } } } + +/** A tree printer which is stingier about vertical whitespace and unnecessary + * punctuation than the standard one. + */ +trait CompactTreePrinter { + val global: Global + + object compactTreePrinters extends { + val trees: global.type = global + } with TreePrinters { + import trees._ + + override def create(writer: PrintWriter): TreePrinter = new TreePrinter(writer) { + // drill down through Blocks and pull out the real statements. + def allStatements(t: Tree): List[Tree] = t match { + case Block(stmts, expr) => (stmts flatMap allStatements) ::: List(expr) + case _ => List(t) + } + + override def printRaw(tree: Tree): Unit = { + // routing supercalls through this for debugging ease + def s() = { + // Console.println("toSuper: " + tree.getClass) + super.printRaw(tree) + } + + tree match { + // labels used for jumps - does not map to valid scala code + case LabelDef(name, params, rhs) => + print("labeldef %s(%s) = ".format(name, params mkString ",")) + printRaw(rhs) + + // target.method(arg) ==> target method arg + case Apply(Select(target, method), List(arg)) => + (target, arg) match { + case (_: Ident, _: Literal | _: Ident) => + printRaw(target) + print(" %s " format method) + printRaw(arg) + case _ => s() + } + + // case Select(Select(_, x), y) if x.toString == "this" => + // print(symName(tree, y)) + // target.unary_! ==> !target + case Select(qualifier, name) => + val n = symName(tree, name) + if (n startsWith "unary_") { + print(n drop 6) + print(qualifier) + } + else s() + + // target.toString() ==> target.toString + case Apply(fn, Nil) => printRaw(fn) + + // if a Block only continues one actual statement, just print it. + case Block(stats, expr) => + allStatements(tree) match { + case List(x) => printRow(List(x), "", ";", "") + case _ => s() + } + + // If thenp or elsep has only one statement, it doesn't need more than one line. + case If(cond, thenp, elsep) => + printRow(List(cond), "if (", "", ") ") + + allStatements(thenp) match { + case List(x) => printRow(List(x), "", ";", "") + case _ => printRaw(thenp) + } + println + allStatements(elsep) match { + case Nil => + case List(x) => printRow(List(x), "else ", "", "") + case xs => print("else ") ; printRaw(elsep) + } + case _ => s() + } + } + // override def symName(tree: Tree, name: Name): String = + // super.symName(tree, name).replaceAll("""^(.*)\.""", "") + } + } + + lazy val compactTreePrinter = compactTreePrinters.create() +} |