summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/ast/TreePrinters.scala
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2010-01-30 06:30:57 +0000
committerPaul Phillips <paulp@improving.org>2010-01-30 06:30:57 +0000
commitc73ab4525e402cbbc0fd745a34c453520c87564c (patch)
tree9283e20e3edc5b0eb68853ba52a5101474dd2037 /src/compiler/scala/tools/nsc/ast/TreePrinters.scala
parentbb149d1b96015d83e58de5ea9b380550267c4f06 (diff)
downloadscala-c73ab4525e402cbbc0fd745a34c453520c87564c.tar.gz
scala-c73ab4525e402cbbc0fd745a34c453520c87564c.tar.bz2
scala-c73ab4525e402cbbc0fd745a34c453520c87564c.zip
A compact tree printer, for primitives like mys...
A compact tree printer, for primitives like myself who do all their debugging in the console and need extraneous information filtered out. New option: -Ycompact-trees. Supply that in conjunction with -Xprint:all and suddenly the output is a (relative) masterpiece of concision. Review by anyone who is game to review such a thing. Community?
Diffstat (limited to 'src/compiler/scala/tools/nsc/ast/TreePrinters.scala')
-rw-r--r--src/compiler/scala/tools/nsc/ast/TreePrinters.scala307
1 files changed, 272 insertions, 35 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/TreePrinters.scala b/src/compiler/scala/tools/nsc/ast/TreePrinters.scala
index 4ba5f17a40..c0b2fb41be 100644
--- a/src/compiler/scala/tools/nsc/ast/TreePrinters.scala
+++ b/src/compiler/scala/tools/nsc/ast/TreePrinters.scala
@@ -8,14 +8,14 @@ package scala.tools.nsc
package ast
import compat.Platform.{EOL => LINE_SEPARATOR}
-import java.io.{OutputStream, PrintWriter, Writer}
+import java.io.{ OutputStream, PrintWriter, StringWriter, Writer }
import symtab.Flags._
import symtab.SymbolTable
abstract class TreePrinters {
-
val trees: SymbolTable
import trees._
+ import treeInfo.{ IsTrue, IsFalse }
final val showOuterTests = false
@@ -29,6 +29,9 @@ abstract class TreePrinters {
def indent = indentMargin += indentStep
def undent = indentMargin -= indentStep
+ protected def doPrintPositions = settings.Xprintpos.value
+ def printPosition(tree: Tree) = if (doPrintPositions) print(tree.pos.show)
+
def println {
out.println()
while (indentMargin > indentString.length())
@@ -75,11 +78,11 @@ abstract class TreePrinters {
def printParam(tree: Tree) {
tree match {
case ValDef(mods, name, tp, rhs) =>
- if (settings.Xprintpos.value) print(tree.pos.show)
+ printPosition(tree)
printAnnotations(tree)
print(symName(tree, name)); printOpt(": ", tp); printOpt(" = ", rhs)
case TypeDef(mods, name, tparams, rhs) =>
- if (settings.Xprintpos.value) print(tree.pos.show)
+ printPosition(tree)
print(symName(tree, name))
printTypeParams(tparams); print(rhs)
}
@@ -93,46 +96,49 @@ abstract class TreePrinters {
printColumn(List(tree), "{", ";", "}")
}
}
- def symName(tree: Tree, name: Name): String =
- if (tree.symbol != null && tree.symbol != NoSymbol) {
- ((if (tree.symbol.isMixinConstructor) "/*"+tree.symbol.owner.name+"*/" else "") +
- tree.symbol.nameString)
- } else name.toString();
+
+ private def symNameInternal(tree: Tree, name: Name, decoded: Boolean): String = {
+ val nameToString: Name => String = if (decoded) _.decode else _.toString
+
+ tree.symbol match {
+ case null | NoSymbol => nameToString(name)
+ case sym =>
+ val prefix = if (sym.isMixinConstructor) "/*%s*/".format(nameToString(sym.owner.name)) else ""
+ prefix + tree.symbol.nameString
+ }
+ }
+
+ def decodedSymName(tree: Tree, name: Name) = symNameInternal(tree, name, true)
+ def symName(tree: Tree, name: Name) = symNameInternal(tree, name, false)
def printOpt(prefix: String, tree: Tree) {
if (!tree.isEmpty) { print(prefix); print(tree) }
}
def printModifiers(tree: Tree, mods: Modifiers) {
- if (tree.symbol == NoSymbol)
- printFlags(mods.flags, mods.privateWithin.toString)
- else if (tree.symbol.privateWithin == NoSymbol ||
- tree.symbol.privateWithin == tree.symbol.owner)
- printFlags(tree.symbol.flags, "")
- else
- printFlags(tree.symbol.flags, tree.symbol.privateWithin.name.toString)
+ def pw = tree.symbol.privateWithin
+ val args =
+ if (tree.symbol == NoSymbol) (mods.flags, mods.privateWithin)
+ else if (pw == NoSymbol || pw == tree.symbol.owner) (tree.symbol.flags, "")
+ else (tree.symbol.flags, pw.name)
+
+ printFlags(args._1, args._2.toString)
}
def printFlags(flags: Long, privateWithin: String) {
var mask: Long = if (settings.debug.value) -1L else PrintableFlags
val s = flagsToString(flags & mask, privateWithin)
- if (s.length() != 0) print(s + " ")
+ if (s != "") print(s + " ")
}
def printAnnotations(tree: Tree) {
- if (!tree.symbol.rawAnnotations.isEmpty) {
- val annots = tree.symbol.annotations
- if (!annots.isEmpty) {
- annots foreach { annot => print("@"+annot+" ") }
- println
- }
- } else {
- val annots = tree.asInstanceOf[MemberDef].mods.annotations
- if (!annots.isEmpty) {
- annots foreach { annot => print("@"+annot+" ") }
- println
- }
- }
+ val annots =
+ if (tree.symbol.rawAnnotations.nonEmpty) tree.symbol.annotations
+ else tree.asInstanceOf[MemberDef].mods.annotations
+
+ annots foreach (annot => print("@"+annot+" "))
+ if (annots.nonEmpty)
+ println
}
def print(str: String) { out.print(str) }
@@ -165,7 +171,7 @@ abstract class TreePrinters {
case ValDef(mods, name, tp, rhs) =>
printAnnotations(tree)
printModifiers(tree, mods)
- print(if (mods.hasFlag(MUTABLE)) "var " else "val ")
+ print(if (mods.isVariable) "var " else "val ")
print(symName(tree, name))
printOpt(": ", tp)
if (!mods.hasFlag(DEFERRED)) {
@@ -389,7 +395,7 @@ abstract class TreePrinters {
}
def print(tree: Tree) {
- if (settings.Xprintpos.value) print(tree.pos.show)
+ printPosition(tree)
printRaw(
if (tree.isDef && tree.symbol != NoSymbol && tree.symbol.isInitialized) {
tree match {
@@ -417,11 +423,242 @@ abstract class TreePrinters {
}
}
+
+ /** A tree printer which is stingier about vertical whitespace and unnecessary
+ * punctuation than the standard one.
+ */
+ class CompactTreePrinter(out: PrintWriter) extends TreePrinter(out) {
+ override def printRow(ts: List[Tree], start: String, sep: String, end: String) {
+ print(start)
+ printSeq(ts)(print)(print(sep))
+ print(end)
+ }
+
+ // 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)
+ }
+
+ def printLogicalOr(t1: (Tree, Boolean), t2: (Tree, Boolean)) =
+ printLogicalOp(t1, t2, "||")
+
+ def printLogicalAnd(t1: (Tree, Boolean), t2: (Tree, Boolean)) =
+ printLogicalOp(t1, t2, "&&")
+
+ def printLogicalOp(t1: (Tree, Boolean), t2: (Tree, Boolean), op: String) = {
+ def maybenot(tvalue: Boolean) = if (tvalue) "" else "!"
+
+ print("%s(" format maybenot(t1._2))
+ printRaw(t1._1)
+ print(") %s %s(".format(op, maybenot(t2._2)))
+ printRaw(t2._1)
+ print(")")
+ }
+
+ override def printRaw(tree: Tree): Unit = {
+ // routing supercalls through this for debugging ease
+ def s() = 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)
+
+ case Ident(name) =>
+ print(decodedSymName(tree, name))
+
+ // target.method(arg) ==> target method arg
+ case Apply(Select(target, method), List(arg)) =>
+ if (method.decode.toString == "||")
+ printLogicalOr(target -> true, arg -> true)
+ else if (method.decode.toString == "&&")
+ printLogicalAnd(target -> true, arg -> true)
+ else (target, arg) match {
+ case (_: Ident, _: Literal | _: Ident) =>
+ printRaw(target)
+ print(" ")
+ printRaw(Ident(method))
+ print(" ")
+ printRaw(arg)
+ case _ => s()
+ }
+
+ // target.unary_! ==> !target
+ case Select(qualifier, name) if (name.decode startsWith "unary_") =>
+ print(name.decode drop 6)
+ printRaw(qualifier)
+
+ case Select(qualifier, name) =>
+ printRaw(qualifier)
+ print(".")
+ print(name.decode)
+
+ // 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) => printRaw(x)
+ case xs => s()
+ }
+
+ // We get a lot of this stuff
+ case If( IsTrue(), x, _) => printRaw(x)
+ case If(IsFalse(), _, x) => printRaw(x)
+
+ case If(cond, IsTrue(), elsep) => printLogicalOr(cond -> true, elsep -> true)
+ case If(cond, IsFalse(), elsep) => printLogicalAnd(cond -> false, elsep -> true)
+ case If(cond, thenp, IsTrue()) => printLogicalOr(cond -> false, thenp -> true)
+ case If(cond, thenp, IsFalse()) => printLogicalAnd(cond -> true, thenp -> true)
+
+ // If thenp or elsep has only one statement, it doesn't need more than one line.
+ case If(cond, thenp, elsep) =>
+ def ifIndented(x: Tree) = {
+ indent ; println ; printRaw(x) ; undent
+ }
+
+ val List(thenStmts, elseStmts) = List(thenp, elsep) map allStatements
+ print("if ("); print(cond); print(")")
+
+ thenStmts match {
+ case List(x: If) => ifIndented(x)
+ case List(x) => printRaw(x)
+ case _ => printRaw(thenp)
+ }
+
+ if (elseStmts.nonEmpty) {
+ print("else")
+ indent ; println
+ elseStmts match {
+ case List(x) => printRaw(x)
+ case _ => printRaw(elsep)
+ }
+ undent ; println
+ }
+ case _ => s()
+ }
+ }
+ }
+
+ /** This must guarantee not to force any evaluation, so we can learn
+ * a little bit about trees in the midst of compilation without altering
+ * the natural course of events.
+ */
+ class SafeTreePrinter(out: PrintWriter) extends TreePrinter(out) {
+ override def print(tree: Tree) {
+ printPosition(tree)
+ printRaw(tree)
+ }
+ private def default(t: Tree) = t.getClass.getName.reverse.takeWhile(_ != '.').reverse
+ private def params(trees: List[Tree]): String = trees map safe mkString ", "
+
+ private def safe(name: Name): String = name.decode
+ private def safe(tree: Tree): String = tree match {
+ case Apply(fn, args) => "%s(%s)".format(safe(fn), params(args))
+ case Select(qual, name) => safe(qual) + "." + safe(name)
+ case This(qual) => safe(qual) + ".this"
+ case Ident(name) => safe(name)
+ case Literal(value) => value.stringValue
+ case _ => "(?: %s)".format(default(tree))
+ }
+
+ override def printRaw(tree: Tree) { print(safe(tree)) }
+ }
+
+ class TreeMatchTemplate {
+ // non-trees defined in Trees
+ //
+ // case class ImportSelector(name: Name, namePos: Int, rename: Name, renamePos: Int)
+ // case class Modifiers(flags: Long, privateWithin: Name, annotations: List[Tree], positions: Map[Long, Position])
+ //
+ def apply(t: Tree): Unit = t match {
+ // eliminated by typer
+ case Annotated(annot, arg) =>
+ case AssignOrNamedArg(lhs, rhs) =>
+ case DocDef(comment, definition) =>
+ case Import(expr, selectors) =>
+
+ // eliminated by refchecks
+ case ModuleDef(mods, name, impl) =>
+
+ // eliminated by erasure
+ case TypeDef(mods, name, tparams, rhs) =>
+ case Typed(expr, tpt) =>
+
+ // eliminated by cleanup
+ case ApplyDynamic(qual, args) =>
+
+ // eliminated by explicitouter
+ case Alternative(trees) =>
+ case Bind(name, body) =>
+ case CaseDef(pat, guard, body) =>
+ case Star(elem) =>
+ case UnApply(fun, args) =>
+
+ // eliminated by lambdalift
+ case Function(vparams, body) =>
+
+ // eliminated by uncurry
+ case AppliedTypeTree(tpt, args) =>
+ case CompoundTypeTree(templ) =>
+ case ExistentialTypeTree(tpt, whereClauses) =>
+ case SelectFromTypeTree(qual, selector) =>
+ case SingletonTypeTree(ref) =>
+ case TypeBoundsTree(lo, hi) =>
+
+ // survivors
+ case Apply(fun, args) =>
+ case ArrayValue(elemtpt, trees) =>
+ case Assign(lhs, rhs) =>
+ case Block(stats, expr) =>
+ case ClassDef(mods, name, tparams, impl) =>
+ case DefDef(mods, name, tparams, vparamss, tpt, rhs) =>
+ case EmptyTree =>
+ case Ident(name) =>
+ case If(cond, thenp, elsep) =>
+ case LabelDef(name, params, rhs) =>
+ case Literal(value) =>
+ case Match(selector, cases) =>
+ case New(tpt) =>
+ case PackageDef(pid, stats) =>
+ case Return(expr) =>
+ case Select(qualifier, selector) =>
+ case Super(qual, mix) =>
+ case Template(parents, self, body) =>
+ case This(qual) =>
+ case Throw(expr) =>
+ case Try(block, catches, finalizer) =>
+ case TypeApply(fun, args) =>
+ case TypeTree() =>
+ case ValDef(mods, name, tpt, rhs) =>
+
+ // missing from the Trees comment
+ case Parens(args) => // only used during parsing
+ case SelectFromArray(qual, name, erasure) => // only used during erasure
+ }
+ }
+
+ private def asStringInternal(t: Tree, f: PrintWriter => TreePrinter): String = {
+ val buffer = new StringWriter()
+ val printer = f(new PrintWriter(buffer))
+ printer.print(t)
+ printer.flush()
+ buffer.toString
+ }
+ def asString(t: Tree): String = asStringInternal(t, create)
+ def asCompactString(t: Tree): String = asStringInternal(t, createCompact)
+
def create(writer: PrintWriter): TreePrinter = new TreePrinter(writer)
def create(stream: OutputStream): TreePrinter = create(new PrintWriter(stream))
- def create(): TreePrinter = {
- create(new PrintWriter(ConsoleWriter))
- }
+ def create(): TreePrinter = create(new PrintWriter(ConsoleWriter))
+
+ def createCompact(writer: PrintWriter): CompactTreePrinter = new CompactTreePrinter(writer)
+ def createCompact(stream: OutputStream): CompactTreePrinter = createCompact(new PrintWriter(stream))
+ def createCompact(): CompactTreePrinter = createCompact(new PrintWriter(ConsoleWriter))
+
/** A writer that writes to the current Console and
* is sensitive to replacement of the Console's
* output stream.