summaryrefslogtreecommitdiff
path: root/src/repl/scala/tools/nsc/interpreter/package.scala
blob: 7934d819b4867d9dfa499a845bf2cf71071c571d (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
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
/* NSC -- new Scala compiler
 * Copyright 2005-2013 LAMP/EPFL
 * @author Paul Phillips
 */

package scala.tools.nsc

import scala.language.implicitConversions
import scala.reflect.{ classTag, ClassTag }
import scala.reflect.runtime.{ universe => ru }
import scala.reflect.{ClassTag, classTag}
import scala.reflect.api.{Mirror, TypeCreator, Universe => ApiUniverse}
import scala.util.control.Exception.catching
import scala.util.Try

/** The main REPL related classes and values are as follows.
 *  In addition to standard compiler classes Global and Settings, there are:
 *
 *  History: an interface for session history.
 *  Completion: an interface for tab completion.
 *  ILoop (formerly InterpreterLoop): The umbrella class for a session.
 *  IMain (formerly Interpreter): Handles the evolving state of the session
 *    and handles submitting code to the compiler and handling the output.
 *  InteractiveReader: how ILoop obtains input.
 *  History: an interface for session history.
 *  Completion: an interface for tab completion.
 *  Power: a repository for more advanced/experimental features.
 *
 *  ILoop contains { in: InteractiveReader, intp: IMain, settings: Settings, power: Power }
 *  InteractiveReader contains { history: History, completion: Completion }
 *  IMain contains { global: Global }
 */
package object interpreter extends ReplConfig with ReplStrings {
  type JFile          = java.io.File
  type JClass         = java.lang.Class[_]
  type JList[T]       = java.util.List[T]
  type JCollection[T] = java.util.Collection[T]
  type JPrintWriter   = java.io.PrintWriter
  type InputStream    = java.io.InputStream
  type OutputStream   = java.io.OutputStream

  val IR = Results

  implicit def postfixOps = scala.language.postfixOps // make all postfix ops in this package compile without warning

  private[interpreter] implicit def javaCharSeqCollectionToScala(xs: JCollection[_ <: CharSequence]): List[String] = {
    import scala.collection.JavaConverters._
    xs.asScala.toList map ("" + _)
  }

  private[nsc] implicit def enrichClass[T](clazz: Class[T]) = new RichClass[T](clazz)
  private[nsc] implicit def enrichAnyRefWithTap[T](x: T) = new TapMaker(x)
  private[nsc] def debugging[T](msg: String)(x: T) = x.tapDebug(msg)

  private val ourClassloader = getClass.getClassLoader

  def staticTypeTag[T: ClassTag]: ru.TypeTag[T] = ru.TypeTag[T](
    ru.runtimeMirror(ourClassloader),
    new TypeCreator {
      def apply[U <: ApiUniverse with Singleton](m: Mirror[U]): U # Type =
        m.staticClass(classTag[T].runtimeClass.getName).toTypeConstructor.asInstanceOf[U # Type]
  })

  /** This class serves to trick the compiler into treating a var
   *  (intp, in ILoop) as a stable identifier.
   */
  implicit class IMainOps(val intp: IMain) {
    import intp._
    import global.{ reporter => _, _ }
    import definitions._

    protected def echo(msg: String) = {
      Console.out println msg
      Console.out.flush()
    }

    def implicitsCommand(line: String): String = {
      def p(x: Any) = intp.reporter.printMessage("" + x)

      // If an argument is given, only show a source with that
      // in its name somewhere.
      val args     = line split "\\s+"
      val filtered = intp.implicitSymbolsBySource filter {
        case (source, syms) =>
          (args contains "-v") || {
            if (line == "") (source.fullName.toString != "scala.Predef")
            else (args exists (source.name.toString contains _))
          }
      }

      if (filtered.isEmpty)
        return "No implicits have been imported other than those in Predef."

      filtered foreach {
        case (source, syms) =>
          p("/* " + syms.size + " implicit members imported from " + source.fullName + " */")

          // This groups the members by where the symbol is defined
          val byOwner = syms groupBy (_.owner)
          val sortedOwners = byOwner.toList sortBy { case (owner, _) => exitingTyper(source.info.baseClasses indexOf owner) }

          sortedOwners foreach {
            case (owner, members) =>
              // Within each owner, we cluster results based on the final result type
              // if there are more than a couple, and sort each cluster based on name.
              // This is really just trying to make the 100 or so implicits imported
              // by default into something readable.
              val memberGroups: List[List[Symbol]] = {
                val groups = members groupBy (_.tpe.finalResultType) toList
                val (big, small) = groups partition (_._2.size > 3)
                val xss = (
                  (big sortBy (_._1.toString) map (_._2)) :+
                  (small flatMap (_._2))
                )

                xss map (xs => xs sortBy (_.name.toString))
              }

              val ownerMessage = if (owner == source) " defined in " else " inherited from "
              p("  /* " + members.size + ownerMessage + owner.fullName + " */")

              memberGroups foreach { group =>
                group foreach (s => p("  " + intp.symbolDefString(s)))
                p("")
              }
          }
          p("")
      }
      ""
    }

    def kindCommandInternal(expr: String, verbose: Boolean): Unit = {
      val catcher = catching(classOf[MissingRequirementError],
                             classOf[ScalaReflectionException])
      def typeFromTypeString: Option[ClassSymbol] = catcher opt {
        exprTyper.typeOfTypeString(expr).typeSymbol.asClass
      }
      def typeFromNameTreatedAsTerm: Option[ClassSymbol] = catcher opt {
        val moduleClass = exprTyper.typeOfExpression(expr).typeSymbol
        moduleClass.linkedClassOfClass.asClass
      }
      def typeFromFullName: Option[ClassSymbol] = catcher opt {
        intp.global.rootMirror.staticClass(expr)
      }
      def typeOfTerm: Option[TypeSymbol] = replInfo(symbolOfLine(expr)).typeSymbol match {
        case sym: TypeSymbol => Some(sym)
        case _ => None
      }
      (typeFromTypeString orElse typeFromNameTreatedAsTerm orElse typeFromFullName orElse typeOfTerm) foreach { sym =>
        val (kind, tpe) = exitingTyper {
          val tpe = sym.tpeHK
          (intp.global.inferKind(NoPrefix)(tpe, sym.owner), tpe)
        }
        echoKind(tpe, kind, verbose)
      }
    }

    def echoKind(tpe: Type, kind: Kind, verbose: Boolean) {
      def typeString(tpe: Type): String = {
        tpe match {
          case TypeRef(_, sym, _) => typeString(sym.info)
          case RefinedType(_, _)  => tpe.toString
          case _                  => tpe.typeSymbol.fullName
        }
      }
      printAfterTyper(typeString(tpe) + "'s kind is " + kind.scalaNotation)
      if (verbose) {
        echo(kind.starNotation)
        echo(kind.description)
      }
    }

    /** TODO -
     *  -n normalize
     *  -l label with case class parameter names
     *  -c complete - leave nothing out
     */
    def typeCommandInternal(expr: String, verbose: Boolean): Unit =
      symbolOfLine(expr) andAlso (echoTypeSignature(_, verbose))

    def printAfterTyper(msg: => String) =
      reporter printUntruncatedMessage exitingTyper(msg)

    private def replInfo(sym: Symbol) =
      if (sym.isAccessor) dropNullaryMethod(sym.info) else sym.info

    def echoTypeStructure(sym: Symbol) =
      printAfterTyper("" + deconstruct.show(replInfo(sym)))

    def echoTypeSignature(sym: Symbol, verbose: Boolean) = {
      if (verbose) echo("// Type signature")
      printAfterTyper("" + replInfo(sym))

      if (verbose) {
        echo("\n// Internal Type structure")
        echoTypeStructure(sym)
      }
    }
  }

  /* An s-interpolator that uses `stringOf(arg)` instead of `String.valueOf(arg)`. */
  private[nsc] implicit class `smart stringifier`(val sc: StringContext) extends AnyVal {
    import StringContext._, runtime.ScalaRunTime.stringOf
    def ss(args: Any*): String = sc.standardInterpolator(treatEscapes, args map stringOf)
  }
  /* Try (body) lastly (more) */
  private[nsc] implicit class `try lastly`[A](val t: Try[A]) extends AnyVal {
    private def effect[X](last: => Unit)(a: X): Try[A] = { last; t }
    def lastly(last: => Unit): Try[A] = t transform (effect(last) _, effect(last) _)
  }
}