summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/ast/Printers.scala
blob: f3def3c80c3676aa7bfc6ae73c2635c9486b2427 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
/* NSC -- new Scala compiler
 * Copyright 2005-2013 LAMP/EPFL
 * @author  Martin Odersky
 */

package scala.tools.nsc
package ast

import java.io.{ OutputStream, PrintWriter, StringWriter, Writer }

trait Printers extends scala.reflect.internal.Printers { this: Global =>

  import treeInfo.{ IsTrue, IsFalse }

  class TreePrinter(out: PrintWriter) extends super.TreePrinter(out) {

    override def print(args: Any*): Unit = args foreach {
      case tree: Tree =>
        printPosition(tree)
        printTree(
            if (tree.isDef && tree.symbol != NoSymbol && tree.symbol.isInitialized) {
              tree match {
                case ClassDef(_, _, _, impl @ Template(ps, noSelfType, body))
                if (tree.symbol.thisSym != tree.symbol) =>
                  ClassDef(tree.symbol, Template(ps, ValDef(tree.symbol.thisSym), body))
                case ClassDef(_, _, _, impl)           => ClassDef(tree.symbol, impl)
                case ModuleDef(_, _, impl)             => ModuleDef(tree.symbol, impl)
                case ValDef(_, _, _, rhs)              => ValDef(tree.symbol, rhs)
                case DefDef(_, _, _, vparamss, _, rhs) => DefDef(tree.symbol, vparamss, rhs)
                case TypeDef(_, _, _, rhs)             => TypeDef(tree.symbol, rhs)
                case _ => tree
              }
            } else tree)
      case unit: CompilationUnit =>
        print("// Scala source: " + unit.source + "\n")
        if (unit.body == null) print("<null>")
        else { print(unit.body); println() }
        println()
        out.flush()
      case arg =>
        super.print(arg)
    }
  }

  // overflow cases missing from TreePrinter in scala.reflect.api
  override def xprintTree(treePrinter: super.TreePrinter, tree: Tree) = tree match {
    case DocDef(comment, definition) =>
      treePrinter.print(comment.raw)
      treePrinter.println()
      treePrinter.print(definition)

    case TypeTreeWithDeferredRefCheck() =>
      treePrinter.print("<tree with deferred refcheck>")

    case SelectFromArray(qualifier, name, _) =>
      treePrinter.print(qualifier, ".<arr>", symName(tree, name))

    case _ =>
      super.xprintTree(treePrinter, tree)
  }

  /** 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))
      printTree(t1._1)
      print(") %s %s(".format(op, maybenot(t2._2)))
      printTree(t2._1)
      print(")")
    }

    override def printTree(tree: Tree): Unit = {
      // routing supercalls through this for debugging ease
      def s() = super.printTree(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 ","))
          printTree(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)  =>
              printTree(target)
              print(" ")
              printTree(Ident(method))
              print(" ")
              printTree(arg)
            case _                        => s()
          }

        // target.unary_! ==> !target
        case Select(qualifier, name) if (name.decode startsWith "unary_") =>
          print(name.decode drop 6)
          printTree(qualifier)

        case Select(qualifier, name) =>
          printTree(qualifier)
          print(".")
          print(quotedName(name, decode = true))

        // target.toString() ==> target.toString
        case Apply(fn, Nil)   => printTree(fn)

        // if a Block only continues one actual statement, just print it.
        case Block(stats, expr) =>
          allStatements(tree) match {
            case List(x)            => printTree(x)
            case xs                 => s()
          }

        // We get a lot of this stuff
        case If( IsTrue(), x, _)        => printTree(x)
        case If(IsFalse(), _, x)        => printTree(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() ; printTree(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)      => printTree(x)
            case _            => printTree(thenp)
          }

          if (elseStmts.nonEmpty) {
            print(" else")
            indent() ; println()
            elseStmts match {
              case List(x)  => printTree(x)
              case _        => printTree(elsep)
            }
            undent() ; println()
          }
        case _        => s()
      }
    }
  }

  def asString(t: Tree): String = render(t, newStandardTreePrinter, settings.printtypes, settings.uniqid, settings.Yshowsymowners, settings.Yshowsymkinds)
  def asCompactString(t: Tree): String = render(t, newCompactTreePrinter, settings.printtypes, settings.uniqid, settings.Yshowsymowners, settings.Yshowsymkinds)
  def asCompactDebugString(t: Tree): String = render(t, newCompactTreePrinter, true, true, true, true)

  def newStandardTreePrinter(writer: PrintWriter): TreePrinter = new TreePrinter(writer)
  def newCompactTreePrinter(writer: PrintWriter): CompactTreePrinter = new CompactTreePrinter(writer)

  override def newTreePrinter(writer: PrintWriter): TreePrinter =
    if (settings.Ycompacttrees) newCompactTreePrinter(writer)
    else newStandardTreePrinter(writer)
  override def newTreePrinter(stream: OutputStream): TreePrinter = newTreePrinter(new PrintWriter(stream))
  override def newTreePrinter(): TreePrinter = newTreePrinter(new PrintWriter(ConsoleWriter))
}