diff options
author | Paul Phillips <paulp@improving.org> | 2010-01-30 06:30:57 +0000 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2010-01-30 06:30:57 +0000 |
commit | c73ab4525e402cbbc0fd745a34c453520c87564c (patch) | |
tree | 9283e20e3edc5b0eb68853ba52a5101474dd2037 /src/compiler/scala/tools/nsc/ast/TreePrinters.scala | |
parent | bb149d1b96015d83e58de5ea9b380550267c4f06 (diff) | |
download | scala-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.scala | 307 |
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. |