summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/AbstractOrMissingHandler.scala41
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/CodeHandlers.scala71
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala125
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/ILoop.scala194
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/ILoopInit.scala131
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/IMain.scala596
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/Imports.scala12
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala8
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/JLineReader.scala47
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/Logger.scala18
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/LoopCommands.scala2
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala47
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/Naming.scala64
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/Power.scala155
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/ReplConfig.scala45
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/ReplProps.scala25
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/ReplReporter.scala22
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/ReplStrings.scala2
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/SimpleReader.scala8
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/package.scala9
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/session/FileBackedHistory.scala13
-rw-r--r--src/compiler/scala/tools/nsc/settings/ScalaSettings.scala1
-rw-r--r--src/partest/scala/tools/partest/ReplTest.scala60
-rw-r--r--src/partest/scala/tools/partest/SigTest.scala51
-rw-r--r--test/files/jvm/interpreter.check6
-rw-r--r--test/files/jvm/interpreter.scala158
-rw-r--r--test/files/run/bug4710.check7
-rw-r--r--test/files/run/bug4710.scala6
-rw-r--r--test/files/run/constrained-types.scala5
-rw-r--r--test/files/run/repl-bare-expr.check36
-rw-r--r--test/files/run/repl-bare-expr.scala16
-rw-r--r--test/files/run/repl-colon-type.check57
-rw-r--r--test/files/run/repl-colon-type.scala23
-rw-r--r--test/files/run/repl-parens.check58
-rw-r--r--test/files/run/repl-parens.scala26
-rw-r--r--test/files/run/repl-paste-2.check3
-rw-r--r--test/files/run/repl-power.check17
-rw-r--r--test/files/run/repl-power.scala10
-rw-r--r--test/files/run/repl-transcript.check3
39 files changed, 1563 insertions, 615 deletions
diff --git a/src/compiler/scala/tools/nsc/interpreter/AbstractOrMissingHandler.scala b/src/compiler/scala/tools/nsc/interpreter/AbstractOrMissingHandler.scala
new file mode 100644
index 0000000000..2f47685757
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/interpreter/AbstractOrMissingHandler.scala
@@ -0,0 +1,41 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2011 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala.tools.nsc
+package interpreter
+
+class AbstractOrMissingHandler[T](onError: String => Unit, value: T) extends PartialFunction[Throwable, T] {
+ def isDefinedAt(t: Throwable) = t match {
+ case _: AbstractMethodError => true
+ case _: NoSuchMethodError => true
+ case _: MissingRequirementError => true
+ case _: NoClassDefFoundError => true
+ case _ => false
+ }
+ def apply(t: Throwable) = t match {
+ case x @ (_: AbstractMethodError | _: NoSuchMethodError | _: NoClassDefFoundError) =>
+ onError("""
+ |Failed to initialize compiler: %s.
+ |This is most often remedied by a full clean and recompile.
+ |Otherwise, your classpath may continue bytecode compiled by
+ |different and incompatible versions of scala.
+ |""".stripMargin.format(x.getClass.getName split '.' last)
+ )
+ x.printStackTrace()
+ value
+ case x: MissingRequirementError =>
+ onError("""
+ |Failed to initialize compiler: %s not found.
+ |** Note that as of 2.8 scala does not assume use of the java classpath.
+ |** For the old behavior pass -usejavacp to scala, or if using a Settings
+ |** object programatically, settings.usejavacp.value = true.""".stripMargin.format(x.req)
+ )
+ value
+ }
+}
+
+object AbstractOrMissingHandler {
+ def apply[T]() = new AbstractOrMissingHandler[T](Console println _, null.asInstanceOf[T])
+}
diff --git a/src/compiler/scala/tools/nsc/interpreter/CodeHandlers.scala b/src/compiler/scala/tools/nsc/interpreter/CodeHandlers.scala
new file mode 100644
index 0000000000..42a47896a2
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/interpreter/CodeHandlers.scala
@@ -0,0 +1,71 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2011 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala.tools.nsc
+package interpreter
+
+import CodeHandlers.NoSuccess
+import scala.util.control.ControlThrowable
+
+/**
+ * The start of a simpler interface for utilizing the compiler with piecemeal
+ * code strings. The "T" here could potentially be a Tree, a Type, a Symbol,
+ * a Boolean, or something even more exotic.
+ */
+trait CodeHandlers[T] {
+ self =>
+
+ // Expressions are composed of operators and operands.
+ def expr(code: String): T
+
+ // A declaration introduces names and assigns them types.
+ // It can form part of a class definition (§5.1) or of a refinement in a compound type (§3.2.7).
+ // (Ed: aka abstract members.)
+ //
+ // ‘val’ ValDcl | ‘var’ VarDcl | ‘def’ FunDcl | ‘type’ {nl} TypeDcl
+ def decl(code: String): T
+
+ // A definition introduces names that denote terms or types.
+ // It can form part of an object or class definition or it can be local to a block.
+ // (Ed: aka concrete members.)
+ //
+ // ‘val’ PatDef | ‘var’ VarDef | ‘def’ FunDef | ‘type’ {nl} TypeDef |
+ // [‘case’] ‘class’ ClassDef | [‘case’] ‘object’ ObjectDef | ‘trait’ TraitDef
+ def defn(code: String): T
+
+ // An import clause has the form import p.I where p is a stable identifier (§3.1) and I is an import expression.
+ def impt(code: String): T
+
+ // Statements occur as parts of blocks and templates.
+ // A statement can be an import, a definition or an expression, or it can be empty.
+ // Statements used in the template of a class definition can also be declarations.
+ def stmt(code: String): T
+ def stmts(code: String): Seq[T]
+
+ object opt extends CodeHandlers[Option[T]] {
+ val handler: PartialFunction[Throwable, Option[T]] = {
+ case _: NoSuccess => None
+ }
+ val handlerSeq: PartialFunction[Throwable, Seq[Option[T]]] = {
+ case _: NoSuccess => Nil
+ }
+
+ def expr(code: String) = try Some(self.expr(code)) catch handler
+ def decl(code: String) = try Some(self.decl(code)) catch handler
+ def defn(code: String) = try Some(self.defn(code)) catch handler
+ def impt(code: String) = try Some(self.impt(code)) catch handler
+ def stmt(code: String) = try Some(self.stmt(code)) catch handler
+ def stmts(code: String) = try (self.stmts(code) map (x => Some(x))) catch handlerSeq
+ }
+}
+
+object CodeHandlers {
+ def incomplete() = throw CodeIncomplete
+ def fail(msg: String) = throw new CodeException(msg)
+
+ trait NoSuccess extends ControlThrowable
+ class CodeException(msg: String) extends RuntimeException(msg) with NoSuccess { }
+ object CodeIncomplete extends CodeException("CodeIncomplete")
+}
diff --git a/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala b/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala
new file mode 100644
index 0000000000..e47eefa85c
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala
@@ -0,0 +1,125 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2011 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala.tools.nsc
+package interpreter
+
+import util.BatchSourceFile
+import ast.parser.Tokens.EOF
+
+trait ExprTyper {
+ val repl: IMain
+ import repl._
+ import global.{ reporter => _, _ }
+ import syntaxAnalyzer.UnitParser
+ import naming.freshInternalVarName
+
+ object codeParser extends { val global: repl.global.type = repl.global } with CodeHandlers[Tree] {
+ def applyRule[T](code: String, rule: UnitParser => T): T = {
+ reporter.reset()
+ val unit = new CompilationUnit(new BatchSourceFile("<console>", code))
+ val scanner = new UnitParser(unit)
+ val result = rule(scanner)
+ if (!reporter.hasErrors)
+ scanner.accept(EOF)
+
+ result
+ }
+
+ def decl(code: String) = CodeHandlers.fail("todo")
+ def defn(code: String) = CodeHandlers.fail("todo")
+ def expr(code: String) = applyRule(code, _.expr())
+ def impt(code: String) = applyRule(code, _.importExpr())
+ def impts(code: String) = applyRule(code, _.importClause())
+ def stmts(code: String) = applyRule(code, _.templateStatSeq(false)._2)
+ def stmt(code: String) = stmts(code) match {
+ case List(t) => t
+ case xs => CodeHandlers.fail("Not a single statement: " + xs.mkString(", "))
+ }
+ }
+
+ /** Parse a line into a sequence of trees. Returns None if the input is incomplete. */
+ def parse(line: String): Option[List[Tree]] = {
+ var isIncomplete = false
+ reporter.withIncompleteHandler((_, _) => isIncomplete = true) {
+ val trees = codeParser.stmts(line)
+ if (reporter.hasErrors) Some(Nil)
+ else if (isIncomplete) None
+ else Some(trees)
+ }
+ }
+
+ // TODO: integrate these into a CodeHandler[Type].
+
+ // XXX literals.
+ // 1) Identifiers defined in the repl.
+ // 2) A path loadable via getModule.
+ // 3) Try interpreting it as an expression.
+ private var typeOfExpressionDepth = 0
+ def typeOfExpression(expr: String, silent: Boolean = true): Option[Type] = {
+ repltrace("typeOfExpression(" + expr + ")")
+ if (typeOfExpressionDepth > 2) {
+ repldbg("Terminating typeOfExpression recursion for expression: " + expr)
+ return None
+ }
+
+ def asQualifiedImport = {
+ val name = expr.takeWhile(_ != '.')
+ importedTermNamed(name) flatMap { sym =>
+ typeOfExpression(sym.fullName + expr.drop(name.length), true)
+ }
+ }
+ def asModule = safeModule(expr) map (_.tpe)
+ def asExpr = {
+ val lhs = freshInternalVarName()
+ val line = "lazy val " + lhs + " =\n" + expr
+
+ interpret(line, true) match {
+ case IR.Success => typeOfExpression(lhs, true)
+ case _ => None
+ }
+ }
+ def evaluate() = {
+ typeOfExpressionDepth += 1
+ try typeOfTerm(expr) orElse asModule orElse asExpr orElse asQualifiedImport
+ finally typeOfExpressionDepth -= 1
+ }
+
+ // Don't presently have a good way to suppress undesirable success output
+ // while letting errors through, so it is first trying it silently: if there
+ // is an error, and errors are desired, then it re-evaluates non-silently
+ // to induce the error message.
+ beSilentDuring(evaluate()) orElse beSilentDuring(typeOfDeclaration(expr)) orElse {
+ if (!silent)
+ evaluate()
+
+ None
+ }
+ }
+ // Since people will be giving us ":t def foo = 5" even though that is not an
+ // expression, we have a means of typing declarations too.
+ private def typeOfDeclaration(code: String): Option[Type] = {
+ repltrace("typeOfDeclaration(" + code + ")")
+ val obname = freshInternalVarName()
+
+ interpret("object " + obname + " {\n" + code + "\n}\n", true) match {
+ case IR.Success =>
+ val sym = symbolOfTerm(obname)
+ if (sym == NoSymbol) None else {
+ // TODO: bitmap$n is not marked synthetic.
+ val decls = sym.tpe.decls.toList filterNot (x => x.isConstructor || x.isPrivate || (x.name.toString contains "$"))
+ repltrace("decls: " + decls)
+ decls.lastOption map (decl => typeCleanser(sym, decl.name))
+ }
+ case _ =>
+ None
+ }
+ }
+ // def compileAndTypeExpr(expr: String): Option[Typer] = {
+ // class TyperRun extends Run {
+ // override def stopPhase(name: String) = name == "superaccessors"
+ // }
+ // }
+}
diff --git a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala
index 74a23d119d..b249d37006 100644
--- a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala
@@ -7,13 +7,12 @@ package scala.tools.nsc
package interpreter
import Predef.{ println => _, _ }
-import java.io.{ BufferedReader, FileReader, PrintWriter }
+import java.io.{ BufferedReader, FileReader }
+import java.util.concurrent.locks.ReentrantLock
import scala.sys.process.Process
import session._
-import scala.tools.nsc.interpreter.{ Results => IR }
-import scala.tools.util.{ SignalManager, Signallable, Javap }
+import scala.tools.util.{ Signallable, Javap }
import scala.annotation.tailrec
-import scala.util.control.Exception.{ ignoring }
import scala.collection.mutable.ListBuffer
import scala.concurrent.ops
import util.{ ClassPath, Exceptional, stringFromWriter, stringFromStream }
@@ -32,17 +31,19 @@ import io.{ File, Sources }
* @author Lex Spoon
* @version 1.2
*/
-class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
+class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter)
extends AnyRef
with LoopCommands
+ with ILoopInit
{
- def this(in0: BufferedReader, out: PrintWriter) = this(Some(in0), out)
- def this() = this(None, new PrintWriter(Console.out, true))
+ def this(in0: BufferedReader, out: JPrintWriter) = this(Some(in0), out)
+ def this() = this(None, new JPrintWriter(Console.out, true))
var in: InteractiveReader = _ // the input stream from which commands come
var settings: Settings = _
var intp: IMain = _
+ def isAsync = !settings.Yreplsync.value
lazy val power = {
val g = intp.global
Power[g.type](this, g)
@@ -79,34 +80,6 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
/** Record a command for replay should the user request a :replay */
def addReplay(cmd: String) = replayCommandStack ::= cmd
- /** Try to install sigint handler: ignore failure. Signal handler
- * will interrupt current line execution if any is in progress.
- *
- * Attempting to protect the repl from accidental exit, we only honor
- * a single ctrl-C if the current buffer is empty: otherwise we look
- * for a second one within a short time.
- */
- private def installSigIntHandler() {
- def onExit() {
- Console.println("") // avoiding "shell prompt in middle of line" syndrome
- sys.exit(1)
- }
- ignoring(classOf[Exception]) {
- SignalManager("INT") = {
- if (intp == null)
- onExit()
- else if (intp.lineManager.running)
- intp.lineManager.cancel()
- else if (in.currentLine != "") {
- // non-empty buffer, so make them hit ctrl-C a second time
- SignalManager("INT") = onExit()
- io.timer(5)(installSigIntHandler()) // and restore original handler if they don't
- }
- else onExit()
- }
- }
- }
-
/** Close the interpreter and set the var to null. */
def closeInterpreter() {
if (intp ne null) {
@@ -143,8 +116,6 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
settings.classpath append addedClasspath
intp = new ILoopInterpreter
- intp.setContextClassLoader()
- installSigIntHandler()
}
/** print a friendly help message */
@@ -184,19 +155,6 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
}
}
- /** Print a welcome message */
- def printWelcome() {
- import Properties._
- val welcomeMsg =
- """|Welcome to Scala %s (%s, Java %s).
- |Type in expressions to have them evaluated.
- |Type :help for more information.""" .
- stripMargin.format(versionString, javaVmName, javaVersion)
- val addendum = if (isReplDebug) "\n" + new java.util.Date else ""
-
- echo(welcomeMsg + addendum)
- }
-
/** Show the history */
lazy val historyCommand = new LoopCommand("history", "show the history (optional num is commands to show)") {
override def usage = "[num]"
@@ -217,11 +175,17 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
}
}
- private def echo(msg: String) = {
+ // When you know you are most likely breaking into the middle
+ // of a line being typed. This softens the blow.
+ protected def echoAndRefresh(msg: String) = {
+ echo("\n" + msg)
+ in.redrawLine()
+ }
+ protected def echo(msg: String) = {
out println msg
out.flush()
}
- private def echoNoNL(msg: String) = {
+ protected def echoNoNL(msg: String) = {
out print msg
out.flush()
}
@@ -389,25 +353,42 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
protected def newJavap() = new Javap(intp.classLoader, new IMain.ReplStrippingWriter(intp)) {
override def tryClass(path: String): Array[Byte] = {
- // Look for Foo first, then Foo$, but if Foo$ is given explicitly,
- // we have to drop the $ to find object Foo, then tack it back onto
- // the end of the flattened name.
- def className = intp flatName path
- def moduleName = (intp flatName path.stripSuffix("$")) + "$"
-
- val bytes = super.tryClass(className)
- if (bytes.nonEmpty) bytes
- else super.tryClass(moduleName)
+ val hd :: rest = path split '.' toList;
+ // If there are dots in the name, the first segment is the
+ // key to finding it.
+ if (rest.nonEmpty) {
+ intp optFlatName hd match {
+ case Some(flat) =>
+ val clazz = flat :: rest mkString "$"
+ val bytes = super.tryClass(clazz)
+ if (bytes.nonEmpty) bytes
+ else super.tryClass(clazz + "$")
+ case _ => super.tryClass(path)
+ }
+ }
+ else {
+ // Look for Foo first, then Foo$, but if Foo$ is given explicitly,
+ // we have to drop the $ to find object Foo, then tack it back onto
+ // the end of the flattened name.
+ def className = intp flatName path
+ def moduleName = (intp flatName path.stripSuffix("$")) + "$"
+
+ val bytes = super.tryClass(className)
+ if (bytes.nonEmpty) bytes
+ else super.tryClass(moduleName)
+ }
}
}
private lazy val javap =
try newJavap()
catch { case _: Exception => null }
+ // Still todo: modules.
private def typeCommand(line: String): Result = {
- intp.typeOfExpression(line) match {
- case Some(tp) => tp.toString
- case _ => "Failed to determine type."
+ if (line.trim == "") ":type <expression>"
+ else intp.typeOfExpression(line, false) match {
+ case Some(tp) => intp.afterTyper(tp.toString)
+ case _ => "" // the error message was already printed
}
}
@@ -508,8 +489,8 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
)
val replayQuestionMessage =
- """|The repl compiler has crashed spectacularly. Shall I replay your
- |session? I can re-run all lines except the last one.
+ """|That entry seems to have slain the compiler. Shall I replay
+ |your session? I can re-run each line except the last one.
|[y/n]
""".trim.stripMargin
@@ -522,17 +503,18 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
if (isReplDebug) "[searching " + sources.path + " for exception contexts...]"
else "[searching for exception contexts...]"
)
- echo(Exceptional(ex).force().context())
- }
- else {
- echo(util.stackTraceString(ex))
}
+ echo(intp.global.throwableAsString(ex))
+
ex match {
case _: NoSuchMethodError | _: NoClassDefFoundError =>
- echo("Unrecoverable error.")
+ echo("\nUnrecoverable error.")
throw ex
case _ =>
- def fn(): Boolean = in.readYesOrNo(replayQuestionMessage, { echo("\nYou must enter y or n.") ; fn() })
+ def fn(): Boolean =
+ try in.readYesOrNo(replayQuestionMessage, { echo("\nYou must enter y or n.") ; fn() })
+ catch { case _: RuntimeException => false }
+
if (fn()) replay()
else echo("\nAbandoning crashed session.")
}
@@ -548,16 +530,21 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
in readLine prompt
}
// return false if repl should exit
- def processLine(line: String): Boolean =
+ def processLine(line: String): Boolean = {
+ if (isAsync) {
+ awaitInitialized()
+ runThunks()
+ }
if (line eq null) false // assume null means EOF
else command(line) match {
case Result(false, _) => false
case Result(_, Some(finalLine)) => addReplay(finalLine) ; true
case _ => true
}
+ }
while (true) {
- try if (!processLine(readOneLine)) return
+ try if (!processLine(readOneLine())) return
catch crashRecovery
}
}
@@ -630,12 +617,13 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
def powerCmd(): Result = {
if (isReplPower) "Already in power mode."
- else enablePowerMode()
+ else enablePowerMode(false)
}
- def enablePowerMode() = {
+ def enablePowerMode(isDuringInit: Boolean) = {
replProps.power setValue true
power.unleash()
- echo(power.banner)
+ if (isDuringInit) asyncMessage(power.banner)
+ else echo(power.banner)
}
def verbosity() = {
@@ -682,11 +670,7 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
}
def transcript(start: String) = {
- // Printing this message doesn't work very well because it's buried in the
- // transcript they just pasted. Todo: a short timer goes off when
- // lines stop coming which tells them to hit ctrl-D.
- //
- // echo("// Detected repl transcript paste: ctrl-D to finish.")
+ echo("\n// Detected repl transcript paste: ctrl-D to finish.\n")
apply(Iterator(start) ++ readWhile(_.trim != PromptString.trim))
}
}
@@ -786,7 +770,7 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
def chooseReader(settings: Settings): InteractiveReader = {
if (settings.Xnojline.value || Properties.isEmacsShell)
SimpleReader()
- else try JLineReader(
+ else try new JLineReader(
if (settings.noCompletion.value) NoCompletion
else new JLineCompletion(intp)
)
@@ -796,7 +780,6 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
SimpleReader()
}
}
-
def process(settings: Settings): Boolean = {
this.settings = settings
createInterpreter()
@@ -804,7 +787,12 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
// sets in to some kind of reader depending on environmental cues
in = in0 match {
case Some(reader) => SimpleReader(reader, out, true)
- case None => chooseReader(settings)
+ case None =>
+ // some post-initialization
+ chooseReader(settings) match {
+ case x: JLineReader => addThunk(x.consoleReader.postInit) ; x
+ case x => x
+ }
}
loadFiles(settings)
@@ -812,21 +800,25 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
if (intp.reporter.hasErrors)
return false
- printWelcome()
- try {
- // this is about the illusion of snappiness. We call initialize()
- // which spins off a separate thread, then print the prompt and try
- // our best to look ready. Ideally the user will spend a
- // couple seconds saying "wow, it starts so fast!" and by the time
- // they type a command the compiler is ready to roll.
- intp.initialize()
- if (isReplPower) {
- echo("Starting in power mode, one moment...\n")
- enablePowerMode()
- }
- loop()
+ // This is about the illusion of snappiness. We call initialize()
+ // which spins off a separate thread, then print the prompt and try
+ // our best to look ready. The interlocking lazy vals tend to
+ // inter-deadlock, so we break the cycle with a single asynchronous
+ // message to an actor.
+ if (isAsync) {
+ intp initialize initializedCallback()
+ createAsyncListener() // listens for signal to run postInitialization
+ }
+ else {
+ intp.initializeSynchronous()
+ postInitialization()
}
+ printWelcome()
+
+ try loop()
+ catch AbstractOrMissingHandler()
finally closeInterpreter()
+
true
}
@@ -867,7 +859,7 @@ object ILoop {
stringFromStream { ostream =>
Console.withOut(ostream) {
- val output = new PrintWriter(new OutputStreamWriter(ostream), true) {
+ val output = new JPrintWriter(new OutputStreamWriter(ostream), true) {
override def write(str: String) = {
// completely skip continuation lines
if (str forall (ch => ch.isWhitespace || ch == '|')) ()
@@ -903,7 +895,7 @@ object ILoop {
stringFromStream { ostream =>
Console.withOut(ostream) {
val input = new BufferedReader(new StringReader(code))
- val output = new PrintWriter(new OutputStreamWriter(ostream), true)
+ val output = new JPrintWriter(new OutputStreamWriter(ostream), true)
val repl = new ILoop(input, output)
if (sets.classpath.isDefault)
@@ -932,7 +924,7 @@ object ILoop {
repl.settings = new Settings(echo)
repl.settings.embeddedDefaults[T]
repl.createInterpreter()
- repl.in = JLineReader(repl)
+ repl.in = new JLineReader(new JLineCompletion(repl))
// rebind exit so people don't accidentally call sys.exit by way of predef
repl.quietRun("""def exit = println("Type :quit to resume program execution.")""")
diff --git a/src/compiler/scala/tools/nsc/interpreter/ILoopInit.scala b/src/compiler/scala/tools/nsc/interpreter/ILoopInit.scala
new file mode 100644
index 0000000000..f2171ad732
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/interpreter/ILoopInit.scala
@@ -0,0 +1,131 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2011 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala.tools.nsc
+package interpreter
+
+import scala.tools.util.SignalManager
+import scala.util.control.Exception.ignoring
+
+/**
+ * Machinery for the asynchronous initialization of the repl.
+ */
+trait ILoopInit {
+ self: ILoop =>
+
+ /** Print a welcome message */
+ def printWelcome() {
+ import Properties._
+ val welcomeMsg =
+ """|Welcome to Scala %s (%s, Java %s).
+ |Type in expressions to have them evaluated.
+ |Type :help for more information.""" .
+ stripMargin.format(versionString, javaVmName, javaVersion)
+ echo(welcomeMsg)
+ replinfo("[info] started at " + new java.util.Date)
+ }
+
+ protected def asyncMessage(msg: String) {
+ if (isReplInfo || isReplPower)
+ echoAndRefresh(msg)
+ }
+
+ /** Try to install sigint handler: ignore failure. Signal handler
+ * will interrupt current line execution if any is in progress.
+ *
+ * Attempting to protect the repl from accidental exit, we only honor
+ * a single ctrl-C if the current buffer is empty: otherwise we look
+ * for a second one within a short time.
+ */
+ protected def installSigIntHandler() {
+ def onExit() {
+ Console.println("") // avoiding "shell prompt in middle of line" syndrome
+ sys.exit(1)
+ }
+ ignoring(classOf[Exception]) {
+ SignalManager("INT") = {
+ if (intp == null)
+ onExit()
+ else if (intp.lineManager.running)
+ intp.lineManager.cancel()
+ else if (in.currentLine != "") {
+ // non-empty buffer, so make them hit ctrl-C a second time
+ SignalManager("INT") = onExit()
+ io.timer(5)(installSigIntHandler()) // and restore original handler if they don't
+ }
+ else onExit()
+ }
+ }
+ }
+
+ private val initLock = new java.util.concurrent.locks.ReentrantLock()
+ private val initCompilerCondition = initLock.newCondition() // signal the compiler is initialized
+ private val initLoopCondition = initLock.newCondition() // signal the whole repl is initialized
+ private val initStart = System.nanoTime
+
+ private def withLock[T](body: => T): T = {
+ initLock.lock()
+ try body
+ finally initLock.unlock()
+ }
+ // a condition used to ensure serial access to the compiler.
+ @volatile private var initIsComplete = false
+ private def elapsed() = "%.3f".format((System.nanoTime - initStart).toDouble / 1000000000L)
+
+ // the method to be called when the interpreter is initialized.
+ // Very important this method does nothing synchronous (i.e. do
+ // not try to use the interpreter) because until it returns, the
+ // repl's lazy val `global` is still locked.
+ protected def initializedCallback() = withLock(initCompilerCondition.signal())
+
+ // Spins off a thread which awaits a single message once the interpreter
+ // has been initialized.
+ protected def createAsyncListener() = {
+ io.spawn {
+ withLock(initCompilerCondition.await())
+ asyncMessage("[info] compiler init time: " + elapsed() + " s.")
+ postInitialization()
+ }
+ }
+
+ // called from main repl loop
+ protected def awaitInitialized() {
+ if (!initIsComplete)
+ withLock { while (!initIsComplete) initLoopCondition.await() }
+ }
+ protected def postInitThunks = List[Option[() => Unit]](
+ Some(intp.setContextClassLoader _),
+ if (isReplPower) Some(() => enablePowerMode(true)) else None,
+ // do this last to avoid annoying uninterruptible startups
+ Some(installSigIntHandler _)
+ ).flatten
+ // called once after init condition is signalled
+ protected def postInitialization() {
+ postInitThunks foreach (f => addThunk(f()))
+ runThunks()
+ initIsComplete = true
+
+ if (isAsync) {
+ asyncMessage("[info] total init time: " + elapsed() + " s.")
+ withLock(initLoopCondition.signal())
+ }
+ }
+ // code to be executed only after the interpreter is initialized
+ // and the lazy val `global` can be accessed without risk of deadlock.
+ private var pendingThunks: List[() => Unit] = Nil
+ protected def addThunk(body: => Unit) = synchronized {
+ pendingThunks :+= (() => body)
+ }
+ protected def runThunks(): Unit = synchronized {
+ if (pendingThunks.nonEmpty)
+ repldbg("Clearing " + pendingThunks.size + " thunks.")
+
+ while (pendingThunks.nonEmpty) {
+ val thunk = pendingThunks.head
+ pendingThunks = pendingThunks.tail
+ thunk()
+ }
+ }
+}
diff --git a/src/compiler/scala/tools/nsc/interpreter/IMain.scala b/src/compiler/scala/tools/nsc/interpreter/IMain.scala
index a364f218da..ebc3146cc7 100644
--- a/src/compiler/scala/tools/nsc/interpreter/IMain.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/IMain.scala
@@ -7,14 +7,17 @@ package scala.tools.nsc
package interpreter
import Predef.{ println => _, _ }
-import java.io.{ PrintWriter }
-import java.lang.reflect
-import java.net.URL
import util.{ Set => _, _ }
-import io.{ AbstractFile, VirtualDirectory }
-import reporters.{ ConsoleReporter, Reporter }
-import symtab.{ Flags, Names }
-import scala.tools.nsc.interpreter.{ Results => IR }
+import scala.collection.{ mutable, immutable }
+import scala.sys.BooleanProp
+import Exceptional.unwrap
+import ScalaClassLoader.URLClassLoader
+import symtab.Flags
+import io.VirtualDirectory
+import scala.tools.nsc.io.AbstractFile
+import reporters._
+import symtab.Flags
+import scala.reflect.generic.Names
import scala.tools.util.PathResolver
import scala.tools.nsc.util.{ ScalaClassLoader, Exceptional }
import ScalaClassLoader.URLClassLoader
@@ -44,7 +47,7 @@ import IMain._
* all variables defined by that code. To extract the result of an
* interpreted line to show the user, a second "result object" is created
* which imports the variables exported by the above object and then
- * exports a single member named "$export". To accomodate user expressions
+ * exports members called "$eval" and "$print". To accomodate user expressions
* that read from variables or methods defined in previous statements, "import"
* statements are used.
*
@@ -57,25 +60,29 @@ import IMain._
* @author Moez A. Abdel-Gawad
* @author Lex Spoon
*/
-class IMain(val settings: Settings, protected val out: PrintWriter) extends Imports {
+class IMain(val settings: Settings, protected val out: JPrintWriter) extends Imports {
imain =>
/** construct an interpreter that reports to Console */
def this(settings: Settings) = this(settings, new NewLinePrintWriter(new ConsoleWriter, true))
def this() = this(new Settings())
- /** whether to print out result lines */
- private[nsc] var printResults: Boolean = true
-
- /** whether to print errors */
- private[nsc] var totalSilence: Boolean = false
-
- private val RESULT_OBJECT_PREFIX = "RequestResult$"
-
+ lazy val repllog: Logger = new Logger {
+ val out: JPrintWriter = imain.out
+ val isInfo: Boolean = BooleanProp keyExists "scala.repl.info"
+ val isDebug: Boolean = BooleanProp keyExists "scala.repl.debug"
+ val isTrace: Boolean = BooleanProp keyExists "scala.repl.trace"
+ }
lazy val formatting: Formatting = new Formatting {
val prompt = Properties.shellPromptString
}
+ lazy val reporter: ConsoleReporter = new ReplReporter(this)
+
import formatting._
+ import reporter.{ printMessage, withoutTruncating }
+
+ private[nsc] var printResults: Boolean = true // whether to print result lines
+ private[nsc] var totalSilence: Boolean = false // whether to print anything
/** directory to save .class files to */
val virtualDirectory = new VirtualDirectory("(memory)", None) {
@@ -89,15 +96,9 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
def show() = pp(this, 0)
}
- /** reporter */
- lazy val reporter: ConsoleReporter = new IMain.ReplReporter(this)
- import reporter.{ printMessage, withoutTruncating }
-
- // not sure if we have some motivation to print directly to console
+ // This exists mostly because using the reporter too early leads to deadlock.
private def echo(msg: String) { Console println msg }
- // protected def defaultImports: List[String] = List("_root_.scala.sys.exit")
-
/** We're going to go to some trouble to initialize the compiler asynchronously.
* It's critical that nothing call into it until it's been initialized or we will
* run into unrecoverable issues, but the perceived repl startup time goes
@@ -107,66 +108,64 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
*/
private val _compiler: Global = newCompiler(settings, reporter)
private var _initializeComplete = false
- def isInitializeComplete = _initializeComplete
-
- private def _initialize(): Boolean = {
- val source = """
- |class $repl_$init {
- | List(1) map (_ + 1)
- |}
- |""".stripMargin
-
- val result = try {
- new _compiler.Run() compileSources List(new BatchSourceFile("<init>", source))
- if (isReplDebug || settings.debug.value) {
- // Can't use printMessage here, it deadlocks
- Console.println("Repl compiler initialized.")
- }
- // addImports(defaultImports: _*)
+ private def _initSources = List(new BatchSourceFile("<init>", "class $repl_$init { }"))
+ private def _initialize() = {
+ try {
+ new _compiler.Run() compileSources _initSources
+ _initializeComplete = true
true
}
- catch {
- case x: AbstractMethodError =>
- printMessage("""
- |Failed to initialize compiler: abstract method error.
- |This is most often remedied by a full clean and recompile.
- |""".stripMargin
- )
- x.printStackTrace()
- false
- case x: MissingRequirementError => printMessage("""
- |Failed to initialize compiler: %s not found.
- |** Note that as of 2.8 scala does not assume use of the java classpath.
- |** For the old behavior pass -usejavacp to scala, or if using a Settings
- |** object programatically, settings.usejavacp.value = true.""".stripMargin.format(x.req)
- )
- false
- }
-
- try result
- finally _initializeComplete = result
+ catch AbstractOrMissingHandler()
}
// set up initialization future
private var _isInitialized: () => Boolean = null
- def initialize() = synchronized {
+ // argument is a thunk to execute after init is done
+ def initialize(postInitSignal: => Unit): Unit = synchronized {
if (_isInitialized == null)
- _isInitialized = scala.concurrent.ops future _initialize()
+ _isInitialized = scala.concurrent.ops future {
+ val result = _initialize()
+ postInitSignal
+ result
+ }
+ }
+ def initializeSynchronous(): Unit = {
+ if (!isInitializeComplete) {
+ _initialize()
+ assert(global != null, global)
+ }
}
+ def isInitializeComplete = _initializeComplete
/** the public, go through the future compiler */
lazy val global: Global = {
- initialize()
-
- // blocks until it is ; false means catastrophic failure
- if (_isInitialized()) _compiler
- else null
+ if (isInitializeComplete) _compiler
+ else {
+ // If init hasn't been called yet you're on your own.
+ if (_isInitialized == null) {
+ repldbg("Warning: compiler accessed before init set up. Assuming no postInit code.")
+ initialize(())
+ }
+ // blocks until it is ; false means catastrophic failure
+ if (_isInitialized()) _compiler
+ else null
+ }
}
@deprecated("Use `global` for access to the compiler instance.", "2.9.0")
lazy val compiler: global.type = global
import global._
+ import definitions.{ ScalaPackage, JavaLangPackage, PredefModule, RootClass }
+ private def privateTreeOps(t: Tree): List[Tree] = {
+ (new Traversable[Tree] {
+ def foreach[U](f: Tree => U): Unit = t foreach { x => f(x) ; () }
+ }).toList
+ }
+
+ // TODO: If we try to make naming a lazy val, we run into big time
+ // scalac unhappiness with what look like cycles. It has not been easy to
+ // reduce, but name resolution clearly takes different paths.
object naming extends {
val global: imain.global.type = imain.global
} with Naming {
@@ -176,6 +175,7 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
if (definedNameMap contains name) freshUserVarName()
else name
}
+ def isInternalVarName(name: Name): Boolean = isInternalVarName("" + name)
}
import naming._
@@ -193,14 +193,11 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
def afterTyper[T](op: => T): T = atPhase(currentRun.typerPhase.next)(op)
/** Temporarily be quiet */
- def beQuietDuring[T](operation: => T): T = {
- val wasPrinting = printResults
- ultimately(printResults = wasPrinting) {
- if (isReplDebug) echo(">> beQuietDuring")
- else printResults = false
-
- operation
- }
+ def beQuietDuring[T](body: => T): T = {
+ val saved = printResults
+ printResults = false
+ try body
+ finally printResults = saved
}
def beSilentDuring[T](operation: => T): T = {
val saved = totalSilence
@@ -211,8 +208,21 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
def quietRun[T](code: String) = beQuietDuring(interpret(code))
+ private def logAndDiscard[T](label: String, alt: => T): PartialFunction[Throwable, T] = {
+ case t => repldbg(label + ": " + t) ; alt
+ }
+
/** whether to bind the lastException variable */
- private var bindLastException = true
+ private var bindExceptions = true
+ /** takes AnyRef because it may be binding a Throwable or an Exceptional */
+ private def withLastExceptionLock[T](body: => T): T = {
+ assert(bindExceptions, "withLastExceptionLock called incorrectly.")
+ bindExceptions = false
+
+ try beQuietDuring(body)
+ catch logAndDiscard("bindLastException", null.asInstanceOf[T])
+ finally bindExceptions = true
+ }
/** A string representing code to be wrapped around all lines. */
private var _executionWrapper: String = ""
@@ -220,31 +230,28 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
def setExecutionWrapper(code: String) = _executionWrapper = code
def clearExecutionWrapper() = _executionWrapper = ""
- /** Temporarily stop binding lastException */
- def withoutBindingLastException[T](operation: => T): T = {
- val wasBinding = bindLastException
- ultimately(bindLastException = wasBinding) {
- bindLastException = false
- operation
- }
- }
-
- protected def createLineManager(): Line.Manager = new Line.Manager
lazy val lineManager = createLineManager()
/** interpreter settings */
lazy val isettings = new ISettings(this)
- /** Instantiate a compiler. Subclasses can override this to
- * change the compiler class used by this interpreter. */
+ /** Create a line manager. Overridable. */
+ protected def createLineManager(): Line.Manager = new Line.Manager
+
+ /** Instantiate a compiler. Overridable. */
protected def newCompiler(settings: Settings, reporter: Reporter) = {
settings.outputDirs setSingleOutput virtualDirectory
settings.exposeEmptyPackage.value = true
+
new Global(settings, reporter)
}
+ /** Parent classloader. Overridable. */
+ protected def parentClassLoader: ClassLoader =
+ settings.explicitParentLoader.getOrElse( this.getClass.getClassLoader() )
+
/** the compiler's classpath, as URL's */
- lazy val compilerClasspath: List[URL] = new PathResolver(settings) asURLs
+ lazy val compilerClasspath = global.classPath.asURLs
/* A single class loader is used for all commands interpreted by this Interpreter.
It would also be possible to create a new class loader for each command
@@ -286,11 +293,6 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
}
}
}
- private def loadByName(s: String): JClass =
- (classLoader tryToInitializeClass s) getOrElse sys.error("Failed to load expected class: '" + s + "'")
-
- protected def parentClassLoader: ClassLoader =
- settings.explicitParentLoader.getOrElse( this.getClass.getClassLoader() )
def getInterpreterClassLoader() = classLoader
@@ -332,7 +334,7 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
/** Stubs for work in progress. */
def handleTypeRedefinition(name: TypeName, old: Request, req: Request) = {
for (t1 <- old.simpleNameOfType(name) ; t2 <- req.simpleNameOfType(name)) {
- DBG("Redefining type '%s'\n %s -> %s".format(name, t1, t2))
+ repldbg("Redefining type '%s'\n %s -> %s".format(name, t1, t2))
}
}
@@ -342,7 +344,7 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
// assertion failed: fatal: <refinement> has owner value x, but a class owner is required
// so DBG is by-name now to keep it in the family. (It also traps the assertion error,
// but we don't want to unnecessarily risk hosing the compiler's internal state.)
- DBG("Redefining term '%s'\n %s -> %s".format(name, t1, t2))
+ repldbg("Redefining term '%s'\n %s -> %s".format(name, t1, t2))
}
}
def recordRequest(req: Request) {
@@ -355,16 +357,15 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
// warning about serially defining companions. It'd be easy
// enough to just redefine them together but that may not always
// be what people want so I'm waiting until I can do it better.
- if (!settings.nowarnings.value) {
- for {
- name <- req.definedNames filterNot (x => req.definedNames contains x.companionName)
- oldReq <- definedNameMap get name.companionName
- newSym <- req.definedSymbols get name
- oldSym <- oldReq.definedSymbols get name.companionName
- } {
- printMessage("warning: previously defined %s is not a companion to %s.".format(oldSym, newSym))
- printMessage("Companions must be defined together; you may wish to use :paste mode for this.")
- }
+ for {
+ name <- req.definedNames filterNot (x => req.definedNames contains x.companionName)
+ oldReq <- definedNameMap get name.companionName
+ newSym <- req.definedSymbols get name
+ oldSym <- oldReq.definedSymbols get name.companionName
+ } {
+ replwarn("warning: previously defined %s is not a companion to %s.".format(
+ stripString("" + oldSym), stripString("" + newSym)))
+ replwarn("Companions must be defined together; you may wish to use :paste mode for this.")
}
// Updating the defined name map
@@ -377,32 +378,20 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
}
}
- /** Parse a line into a sequence of trees. Returns None if the input is incomplete. */
- def parse(line: String): Option[List[Tree]] = {
- var justNeedsMore = false
- reporter.withIncompleteHandler((pos,msg) => {justNeedsMore = true}) {
- // simple parse: just parse it, nothing else
- def simpleParse(code: String): List[Tree] = {
- reporter.reset()
- val unit = new CompilationUnit(new BatchSourceFile("<console>", code))
- val scanner = new syntaxAnalyzer.UnitParser(unit)
-
- scanner.templateStatSeq(false)._2
- }
- val trees = simpleParse(line)
-
- if (reporter.hasErrors) Some(Nil) // the result did not parse, so stop
- else if (justNeedsMore) None
- else Some(trees)
- }
- }
+ private[nsc] def replwarn(msg: => String): Unit =
+ if (!settings.nowarnings.value)
+ printMessage(msg)
def isParseable(line: String): Boolean = {
beSilentDuring {
- parse(line) match {
+ try parse(line) match {
case Some(xs) => xs.nonEmpty // parses as-is
case None => true // incomplete
}
+ catch { case x: Exception => // crashed the compiler
+ replwarn("Exception in isParseable(\"" + line + "\"): " + x)
+ false
+ }
}
}
@@ -425,45 +414,115 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
*/
private def buildRequest(line: String, trees: List[Tree]): Request = new Request(line, trees)
+ // rewriting "5 // foo" to "val x = { 5 // foo }" creates broken code because
+ // the close brace is commented out. Strip single-line comments.
+ // ... but for error message output reasons this is not used, and rather than
+ // enclosing in braces it is constructed like "val x =\n5 // foo".
+ private def removeComments(line: String): String = {
+ showCodeIfDebugging(line) // as we're about to lose our // show
+ line.lines map (s => s indexOf "//" match {
+ case -1 => s
+ case idx => s take idx
+ }) mkString "\n"
+ }
+ private def safePos(t: Tree, alt: Int): Int =
+ try t.pos.startOrPoint
+ catch { case _: UnsupportedOperationException => alt }
+
+ // Given an expression like 10 * 10 * 10 we receive the parent tree positioned
+ // at a '*'. So look at each subtree and find the earliest of all positions.
+ private def earliestPosition(tree: Tree): Int = {
+ var pos = Int.MaxValue
+ tree foreach { t =>
+ pos = math.min(pos, safePos(t, Int.MaxValue))
+ }
+ pos
+ }
+
private def requestFromLine(line: String, synthetic: Boolean): Either[IR.Result, Request] = {
- val trees = parse(indentCode(line)) match {
+ val content = indentCode(line)
+ val trees = parse(content) match {
case None => return Left(IR.Incomplete)
case Some(Nil) => return Left(IR.Error) // parse error or empty input
case Some(trees) => trees
}
-
- // use synthetic vars to avoid filling up the resXX slots
- def varName = if (synthetic) freshInternalVarName() else freshUserVarName()
-
- // Treat a single bare expression specially. This is necessary due to it being hard to
- // modify code at a textual level, and it being hard to submit an AST to the compiler.
- if (trees.size == 1) trees.head match {
- case _:Assign => // we don't want to include assignments
- case _:TermTree | _:Ident | _:Select => // ... but do want these as valdefs.
- requestFromLine("val %s =\n%s".format(varName, line), synthetic) match {
+ repltrace(
+ trees map { t =>
+ privateTreeOps(t) map { t0 => t0.getClass + " at " + safePos(t0, -1) + "\n" }
+ } mkString
+ )
+ // If the last tree is a bare expression, pinpoint where it begins using the
+ // AST node position and snap the line off there. Rewrite the code embodied
+ // by the last tree as a ValDef instead, so we can access the value.
+ trees.last match {
+ case _:Assign => // we don't want to include assignments
+ case _:TermTree | _:Ident | _:Select => // ... but do want other unnamed terms.
+ val varName = if (synthetic) freshInternalVarName() else freshUserVarName()
+ val rewrittenLine = (
+ // In theory this would come out the same without the 1-specific test, but
+ // it's a cushion against any more sneaky parse-tree position vs. code mismatches:
+ // this way such issues will only arise on multiple-statement repl input lines,
+ // which most people don't use.
+ if (trees.size == 1) "val " + varName + " =\n" + content
+ else {
+ // The position of the last tree
+ val lastpos0 = earliestPosition(trees.last)
+ // Oh boy, the parser throws away parens so "(2+2)" is mispositioned.
+ // So until we can fix the parser we'll have to go trawling.
+ val adjustment = ((content take lastpos0).reverse takeWhile { ch =>
+ ch.isWhitespace || ch == '(' || ch == ')'
+ }).length
+ val lastpos = lastpos0 - adjustment
+
+ // the source code split at the laboriously determined position.
+ val (l1, l2) = content splitAt lastpos
+ val prefix = if (l1.trim == "") "" else l1 + ";\n"
+ // Note to self: val source needs to have this precise structure so that
+ // error messages print the user-submitted part without the "val res0 = " part.
+ val combined = prefix + "val " + varName + " =\n" + l2
+
+ repldbg(List(
+ " line" -> line,
+ " content" -> content,
+ " was" -> l2,
+ "combined" -> combined) map {
+ case (label, s) => label + ": '" + s + "'"
+ } mkString "\n"
+ )
+ combined
+ }
+ )
+ // Rewriting "foo ; bar ; 123"
+ // to "foo ; bar ; val resXX = 123"
+ requestFromLine(rewrittenLine, synthetic) match {
case Right(req) => return Right(req withOriginalLine line)
case x => return x
}
- case _ =>
+ case _ =>
}
-
- // figure out what kind of request
Right(buildRequest(line, trees))
}
+ def typeCleanser(sym: Symbol, memberName: Name): Type = {
+ // the types are all =>T; remove the =>
+ val tp1 = afterTyper(sym.info.nonPrivateDecl(memberName).tpe match {
+ case NullaryMethodType(tp) => tp
+ case tp => tp
+ })
+ // normalize non-public types so we don't see protected aliases like Self
+ afterTyper(tp1 match {
+ case TypeRef(_, sym, _) if !sym.isPublic => tp1.normalize
+ case tp => tp
+ })
+ }
+
/**
- * Interpret one line of input. All feedback, including parse errors
- * and evaluation results, are printed via the supplied compiler's
- * reporter. Values defined are available for future interpreted
- * strings.
- *
+ * Interpret one line of input. All feedback, including parse errors
+ * and evaluation results, are printed via the supplied compiler's
+ * reporter. Values defined are available for future interpreted strings.
*
- * The return value is whether the line was interpreter successfully,
- * e.g. that there were no parse errors.
- *
- *
- * @param line ...
- * @return ...
+ * The return value is whether the line was interpreter successfully,
+ * e.g. that there were no parse errors.
*/
def interpret(line: String): IR.Result = interpret(line, false)
def interpret(line: String, synthetic: Boolean): IR.Result = {
@@ -481,6 +540,9 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
if (succeeded) {
if (printResults)
show()
+ else if (isReplDebug) // show quiet-mode activity
+ printMessage(result.trim.lines map ("[quiet] " + _) mkString "\n")
+
// Book-keeping. Have to record synthetic requests too,
// as they may have been issued for information, e.g. :type
recordRequest(req)
@@ -523,7 +585,7 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
)
bindRep.callOpt("set", value) match {
case Some(_) => interpret("val %s = %s.value".format(name, bindRep.evalPath))
- case _ => DBG("Set failed in bind(%s, %s, %s)".format(name, boundType, value)) ; IR.Error
+ case _ => repldbg("Set failed in bind(%s, %s, %s)".format(name, boundType, value)) ; IR.Error
}
}
def rebind(p: NamedParam): IR.Result = {
@@ -543,7 +605,8 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
def quietBind(p: NamedParam): IR.Result = beQuietDuring(bind(p))
def bind(p: NamedParam): IR.Result = bind(p.name, p.tpe, p.value)
def bind[T: Manifest](name: String, value: T): IR.Result = bind((name, value))
- def bindValue(x: Any): IR.Result = bind(freshUserVarName(), TypeStrings.fromValue(x), x)
+ def bindValue(x: Any): IR.Result = bindValue(freshUserVarName(), x)
+ def bindValue(name: String, x: Any): IR.Result = bind(name, TypeStrings.fromValue(x), x)
/** Reset this interpreter, forgetting all user-specified requests. */
def reset() {
@@ -571,11 +634,40 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
class ReadEvalPrint(lineId: Int) {
def this() = this(freshLineId())
- val packageName = "$line" + lineId
- val readName = "$read"
- val evalName = "$eval"
- val printName = "$print"
- val valueMethod = "$result" // no-args method giving result
+ val packageName = sessionNames.line + lineId
+ val readName = sessionNames.read
+ val evalName = sessionNames.eval
+ val printName = sessionNames.print
+
+ class LineExceptional(ex: Throwable) extends Exceptional(ex) {
+ private def showReplInternal = isettings.showInternalStackTraces
+
+ override def spanFn(frame: JavaStackFrame) =
+ if (showReplInternal) super.spanFn(frame)
+ else !(frame.className startsWith evalPath)
+
+ override def contextPrelude = super.contextPrelude + (
+ if (showReplInternal) ""
+ else "/* The repl internal portion of the stack trace is elided. */\n"
+ )
+ }
+ def bindError(t: Throwable) = {
+ if (!bindExceptions) // avoid looping if already binding
+ throw t
+
+ val unwrapped = unwrap(t)
+ withLastExceptionLock {
+ if (opt.richExes) {
+ val ex = new LineExceptional(unwrapped)
+ bind[Exceptional]("lastException", ex)
+ ex.contextHead + "\n(access lastException for the full trace)"
+ }
+ else {
+ bind[Throwable]("lastException", unwrapped)
+ util.stackTraceString(unwrapped)
+ }
+ }
+ }
// TODO: split it out into a package object and a regular
// object and we can do that much less wrapping.
@@ -593,13 +685,13 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
def callOpt(name: String, args: Any*): Option[AnyRef] =
try Some(call(name, args: _*))
- catch { case ex: Exception =>
- quietBind("lastException", ex)
- None
- }
+ catch { case ex: Exception => bindError(ex) ; None }
- lazy val evalClass = loadByName(evalPath)
- lazy val evalValue = callOpt(valueMethod)
+ private def load(s: String): Class[_] =
+ (classLoader tryToInitializeClass s) getOrElse sys.error("Failed to load expected class: '" + s + "'")
+
+ lazy val evalClass = load(evalPath)
+ lazy val evalValue = callOpt(evalName)
def compile(source: String): Boolean = compileAndSaveRun("<console>", source)
def lineAfterTyper[T](op: => T): T = {
@@ -617,17 +709,10 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
lineAfterTyper(sym.info member newTermName(name))
}
}
-
- // def compileAndTypeExpr(expr: String): Option[Typer] = {
- // class TyperRun extends Run {
- // override def stopPhase(name: String) = name == "superaccessors"
- // }
- // }
private var lastRun: Run = _
- private def evalMethod(name: String) = {
- val methods = evalClass.getMethods filter (_.getName == name)
- assert(methods.size == 1, "Internal error - eval object method " + name + " is overloaded: " + methods)
- methods.head
+ private def evalMethod(name: String) = evalClass.getMethods filter (_.getName == name) match {
+ case Array(method) => method
+ case xs => sys.error("Internal error: eval object " + evalClass + ", " + xs.mkString("\n", "\n", ""))
}
private def compileAndSaveRun(label: String, code: String) = {
showCodeIfDebugging(code)
@@ -660,6 +745,7 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
/** def and val names */
def termNames = handlers flatMap (_.definesTerm)
def typeNames = handlers flatMap (_.definesType)
+ def definedOrImported = handlers flatMap (_.definedOrImported)
/** Code to import bound names from previous lines - accessPath is code to
* append to objectName to access anything bound by request.
@@ -678,6 +764,9 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
def fullFlatName(name: String) =
lineRep.readPath + accessPath.replace('.', '$') + "$" + name
+ /** The unmangled symbol name, but supplemented with line info. */
+ def disambiguated(name: Name): String = name + " (in " + lineRep + ")"
+
/** Code to access a variable with the specified name */
def fullPath(vname: Name): String = fullPath(vname.toString)
@@ -688,7 +777,7 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
private object ObjectSourceCode extends CodeAssembler[MemberHandler] {
val preamble = """
|object %s {
- | %s%s
+ |%s%s
""".stripMargin.format(lineRep.readName, importsPreamble, indentCode(toCompute))
val postamble = importsTrailer + "\n}"
val generate = (m: MemberHandler) => m extraCodeToEvaluate Request.this
@@ -704,9 +793,9 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
case Some(vname) if typeOf contains vname =>
"""
|lazy val $result = {
- | $export
| %s
- |}""".stripMargin.format(fullPath(vname))
+ | %s
+ |}""".stripMargin.format(lineRep.printName, fullPath(vname))
case _ => ""
}
// first line evaluates object to make sure constructor is run
@@ -714,11 +803,12 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
val preamble = """
|object %s {
| %s
- | val $export: String = %s {
+ | val %s: String = %s {
| %s
| (""
""".stripMargin.format(
- lineRep.evalName, evalResult, executionWrapper, lineRep.readName + accessPath
+ lineRep.evalName, evalResult, lineRep.printName,
+ executionWrapper, lineRep.readName + accessPath
)
val postamble = """
@@ -751,7 +841,9 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
typesOfDefinedTerms
// compile the result-extraction object
- lineRep compile ResultObjectSourceCode(handlers)
+ beSilentDuring {
+ lineRep compile ResultObjectSourceCode(handlers)
+ }
}
}
@@ -762,25 +854,13 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
def lookupTypeOf(name: Name) = typeOf.getOrElse(name, typeOf(global.encode(name.toString)))
def simpleNameOfType(name: TypeName) = (compilerTypeOf get name) map (_.typeSymbol.simpleName)
- private def typeMap[T](f: Type => T): Map[Name, T] = {
- def toType(name: Name): T = {
- // the types are all =>T; remove the =>
- val tp1 = lineAfterTyper(resultSymbol.info.nonPrivateDecl(name).tpe match {
- case NullaryMethodType(tp) => tp
- case tp => tp
- })
- // normalize non-public types so we don't see protected aliases like Self
- lineAfterTyper(tp1 match {
- case TypeRef(_, sym, _) if !sym.isPublic => f(tp1.normalize)
- case tp => f(tp)
- })
- }
- termNames ++ typeNames map (x => x -> toType(x)) toMap
- }
+ private def typeMap[T](f: Type => T): Map[Name, T] =
+ termNames ++ typeNames map (x => x -> f(typeCleanser(resultSymbol, x))) toMap
+
/** Types of variables defined by this request. */
lazy val compilerTypeOf = typeMap[Type](x => x)
/** String representations of same. */
- lazy val typeOf = typeMap[String](_.toString)
+ lazy val typeOf = typeMap[String](tp => afterTyper(tp.toString))
// lazy val definedTypes: Map[Name, Type] = {
// typeNames map (x => x -> afterTyper(resultSymbol.info.nonPrivateDecl(x).tpe)) toMap
@@ -793,50 +873,17 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
lazy val typesOfDefinedTerms: Map[Name, Type] =
termNames map (x => x -> applyToResultMember(x, _.tpe)) toMap
- private def bindExceptionally(t: Throwable) = {
- val ex: Exceptional =
- if (isettings.showInternalStackTraces) Exceptional(t)
- else new Exceptional(t) {
- override def spanFn(frame: JavaStackFrame) = !(frame.className startsWith lineRep.evalPath)
- override def contextPrelude = super.contextPrelude + "/* The repl internal portion of the stack trace is elided. */\n"
- }
-
- quietBind("lastException", ex)
- ex.contextHead + "\n(access lastException for the full trace)"
- }
- private def bindUnexceptionally(t: Throwable) = {
- quietBind("lastException", t)
- stackTraceString(t)
- }
-
/** load and run the code using reflection */
def loadAndRun: (String, Boolean) = {
import interpreter.Line._
- def handleException(t: Throwable) = {
- /** We turn off the binding to accomodate ticket #2817 */
- withoutBindingLastException {
- val message =
- if (opt.richExes) bindExceptionally(unwrap(t))
- else bindUnexceptionally(unwrap(t))
-
- (message, false)
- }
- }
-
try {
- val execution = lineManager.set(originalLine)(lineRep call "$export")
+ val execution = lineManager.set(originalLine)(lineRep call sessionNames.print)
execution.await()
execution.state match {
case Done => ("" + execution.get(), true)
- case Threw =>
- val ex = execution.caught()
- if (isReplDebug)
- ex.printStackTrace()
-
- if (bindLastException) handleException(ex)
- else throw ex
+ case Threw => (lineRep.bindError(execution.caught()), false)
case Cancelled => ("Execution interrupted by signal.\n", false)
case Running => ("Execution still running! Seems impossible.", false)
}
@@ -860,14 +907,17 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
case _ => naming.mostRecentVar
})
- private def requestForName(name: Name): Option[Request] = {
+ def requestForName(name: Name): Option[Request] = {
assert(definedNameMap != null, "definedNameMap is null")
definedNameMap get name
}
- private def requestForIdent(line: String): Option[Request] =
+ def requestForIdent(line: String): Option[Request] =
requestForName(newTermName(line)) orElse requestForName(newTypeName(line))
+ def requestHistoryForName(name: Name): List[Request] =
+ prevRequests.toList.reverse filter (_.definedNames contains name)
+
def safeClass(name: String): Option[Symbol] = {
try Some(definitions.getClass(newTypeName(name)))
catch { case _: MissingRequirementError => None }
@@ -918,44 +968,11 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
}
}
- // XXX literals.
- // 1) Identifiers defined in the repl.
- // 2) A path loadable via getModule.
- // 3) Try interpreting it as an expression.
- private var typeOfExpressionDepth = 0
- def typeOfExpression(expr: String): Option[Type] = {
- DBG("typeOfExpression(" + expr + ")")
- if (typeOfExpressionDepth > 2) {
- DBG("Terminating typeOfExpression recursion for expression: " + expr)
- return None
- }
-
- def asQualifiedImport = {
- val name = expr.takeWhile(_ != '.')
- importedTermNamed(name) flatMap { sym =>
- typeOfExpression(sym.fullName + expr.drop(name.length))
- }
- }
- def asModule = safeModule(expr) map (_.tpe)
- def asExpr = beSilentDuring {
- val lhs = freshInternalVarName()
- val line = "lazy val " + lhs + " = { " + expr + " } "
-
- interpret(line, true) match {
- case IR.Success => typeOfExpression(lhs)
- case _ => None
- }
- }
-
- typeOfExpressionDepth += 1
- try typeOfTerm(expr) orElse asModule orElse asExpr orElse asQualifiedImport
- finally typeOfExpressionDepth -= 1
+ private object exprTyper extends { val repl: IMain.this.type = imain } with ExprTyper { }
+ def parse(line: String): Option[List[Tree]] = exprTyper.parse(line)
+ def typeOfExpression(expr: String, silent: Boolean = true): Option[Type] = {
+ exprTyper.typeOfExpression(expr, silent)
}
- // def compileAndTypeExpr(expr: String): Option[Typer] = {
- // class TyperRun extends Run {
- // override def stopPhase(name: String) = name == "superaccessors"
- // }
- // }
protected def onlyTerms(xs: List[Name]) = xs collect { case x: TermName => x }
protected def onlyTypes(xs: List[Name]) = xs collect { case x: TypeName => x }
@@ -1001,20 +1018,16 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
/** Secret bookcase entrance for repl debuggers: end the line
* with "// show" and see what's going on.
*/
- if (code.lines exists (_.trim endsWith "// show")) {
+ if (repllog.isTrace || (code.lines exists (_.trim endsWith "// show"))) {
echo(code)
- parse(code) foreach (ts => ts foreach (t => withoutUnwrapping(DBG(asCompactString(t)))))
+ parse(code) foreach (ts => ts foreach (t => withoutUnwrapping(repldbg(asCompactString(t)))))
}
}
// debugging
def debugging[T](msg: String)(res: T) = {
- DBG(msg + " " + res)
+ repldbg(msg + " " + res)
res
}
- def DBG(s: => String) = if (isReplDebug) {
- try repldbg(s)
- catch { case x: AssertionError => repldbg("Assertion error printing debug string:\n " + x) }
- }
}
/** Utility methods for the Interpreter. */
@@ -1053,8 +1066,8 @@ object IMain {
else str
}
}
- abstract class StrippingTruncatingWriter(out: PrintWriter)
- extends PrintWriter(out)
+ abstract class StrippingTruncatingWriter(out: JPrintWriter)
+ extends JPrintWriter(out)
with StrippingWriter
with TruncatingWriter {
self =>
@@ -1068,29 +1081,6 @@ object IMain {
def isStripping = isettings.unwrapStrings
def isTruncating = reporter.truncationOK
- def stripImpl(str: String): String = {
- val cleaned = stripString(str)
- var ctrlChars = 0
- cleaned map { ch =>
- if (ch.isControl && !ch.isWhitespace) {
- ctrlChars += 1
- if (ctrlChars > 5) return "[line elided for control chars: possibly a scala signature]"
- else '?'
- }
- else ch
- }
- }
- }
-
- class ReplReporter(intp: IMain) extends ConsoleReporter(intp.settings, null, new ReplStrippingWriter(intp)) {
- override def printMessage(msg: String) {
- // Avoiding deadlock when the compiler starts logging before
- // the lazy val is done.
- if (intp.isInitializeComplete) {
- if (intp.totalSilence) ()
- else super.printMessage(msg)
- }
- else Console.println(msg)
- }
+ def stripImpl(str: String): String = naming.unmangle(str)
}
}
diff --git a/src/compiler/scala/tools/nsc/interpreter/Imports.scala b/src/compiler/scala/tools/nsc/interpreter/Imports.scala
index b2706330e7..10b3d82d69 100644
--- a/src/compiler/scala/tools/nsc/interpreter/Imports.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/Imports.scala
@@ -15,6 +15,9 @@ trait Imports {
import definitions.{ ScalaPackage, JavaLangPackage, PredefModule }
import memberHandlers._
+ def isNoImports = settings.noimports.value
+ def isNoPredef = false // settings.nopredef.value
+
/** Synthetic import handlers for the language defined imports. */
private def makeWildcardImportHandler(sym: Symbol): ImportHandler = {
val hd :: tl = sym.fullName.split('.').toList map newTermName
@@ -88,7 +91,7 @@ trait Imports {
* 2. A code fragment that should go after the code
* of the new request.
*
- * 3. An access path which can be traverested to access
+ * 3. An access path which can be traversed to access
* any bindings inside code wrapped by #1 and #2 .
*
* The argument is a set of Names that need to be imported.
@@ -114,12 +117,11 @@ trait Imports {
* 'wanted' is the set of names that need to be imported.
*/
def select(reqs: List[ReqAndHandler], wanted: Set[Name]): List[ReqAndHandler] = {
- val isWanted = wanted contains _
// Single symbol imports might be implicits! See bug #1752. Rather than
// try to finesse this, we will mimic all imports for now.
def keepHandler(handler: MemberHandler) = handler match {
case _: ImportHandler => true
- case x => x.definesImplicit || (x.definedNames exists isWanted)
+ case x => x.definesImplicit || (x.definedNames exists wanted)
}
reqs match {
@@ -157,7 +159,7 @@ trait Imports {
// If the user entered an import, then just use it; add an import wrapping
// level if the import might conflict with some other import
case x: ImportHandler =>
- if (x.importsWildcard || (currentImps exists (x.importedNames contains _)))
+ if (x.importsWildcard || currentImps.exists(x.importedNames contains _))
addWrapper()
code append (x.member + "\n")
@@ -175,7 +177,7 @@ trait Imports {
for (imv <- x.definedNames) {
if (currentImps contains imv) addWrapper()
- code append ("import %s\n" format (req fullPath imv))
+ code append ("import " + (req fullPath imv) + "\n")
currentImps += imv
}
}
diff --git a/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala b/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala
index b7baea57fc..8b387823ff 100644
--- a/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala
@@ -18,7 +18,7 @@ class JLineCompletion(val intp: IMain) extends Completion with CompletionOutput
import global._
import definitions.{ PredefModule, RootClass, AnyClass, AnyRefClass, ScalaPackage, JavaLangPackage }
type ExecResult = Any
- import intp.{ DBG, debugging, afterTyper }
+ import intp.{ debugging, afterTyper }
// verbosity goes up with consecutive tabs
private var verbosity: Int = 0
@@ -325,7 +325,7 @@ class JLineCompletion(val intp: IMain) extends Completion with CompletionOutput
// This is jline's entry point for completion.
override def complete(buf: String, cursor: Int): Candidates = {
verbosity = if (isConsecutiveTabs(buf, cursor)) verbosity + 1 else 0
- DBG("\ncomplete(%s, %d) last = (%s, %d), verbosity: %s".format(buf, cursor, lastBuf, lastCursor, verbosity))
+ repldbg("\ncomplete(%s, %d) last = (%s, %d), verbosity: %s".format(buf, cursor, lastBuf, lastCursor, verbosity))
// we don't try lower priority completions unless higher ones return no results.
def tryCompletion(p: Parsed, completionFunction: Parsed => List[String]): Option[Candidates] = {
@@ -338,7 +338,7 @@ class JLineCompletion(val intp: IMain) extends Completion with CompletionOutput
val advance = commonPrefix(winners)
lastCursor = p.position + advance.length
lastBuf = (buf take p.position) + advance
- DBG("tryCompletion(%s, _) lastBuf = %s, lastCursor = %s, p.position = %s".format(
+ repldbg("tryCompletion(%s, _) lastBuf = %s, lastCursor = %s, p.position = %s".format(
p, lastBuf, lastCursor, p.position))
p.position
}
@@ -369,7 +369,7 @@ class JLineCompletion(val intp: IMain) extends Completion with CompletionOutput
}
catch {
case ex: Exception =>
- DBG("Error: complete(%s, %s) provoked %s".format(buf, cursor, ex))
+ repldbg("Error: complete(%s, %s) provoked %s".format(buf, cursor, ex))
Candidates(cursor, List(" ", "<completion error: " + ex.getMessage + ">"))
}
}
diff --git a/src/compiler/scala/tools/nsc/interpreter/JLineReader.scala b/src/compiler/scala/tools/nsc/interpreter/JLineReader.scala
index d222a80196..2e3dc506c6 100644
--- a/src/compiler/scala/tools/nsc/interpreter/JLineReader.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/JLineReader.scala
@@ -13,9 +13,14 @@ import scala.collection.JavaConverters._
import Completion._
import io.Streamable.slurp
-/** Reads from the console using JLine */
-class JLineReader(val completion: Completion) extends InteractiveReader {
+/**
+ * Reads from the console using JLine.
+ */
+class JLineReader(_completion: => Completion) extends InteractiveReader {
val interactive = true
+ val consoleReader = new JLineConsoleReader()
+
+ lazy val completion = _completion
lazy val history: JLineHistory = JLineHistory()
lazy val keyBindings =
try KeyBinding parse slurp(term.getDefaultBindings)
@@ -44,34 +49,30 @@ class JLineReader(val completion: Completion) extends InteractiveReader {
}
def eraseLine() = consoleReader.resetPromptLine("", "", 0)
def redrawLineAndFlush(): Unit = { flush() ; drawLine() ; flush() }
+ // override def readLine(prompt: String): String
- this setBellEnabled false
- if (history ne NoHistory)
- this setHistory history
+ // A hook for running code after the repl is done initializing.
+ lazy val postInit: Unit = {
+ this setBellEnabled false
+ if (history ne NoHistory)
+ this setHistory history
- if (completion ne NoCompletion) {
- val argCompletor: ArgumentCompleter =
- new ArgumentCompleter(new JLineDelimiter, scalaToJline(completion.completer()))
- argCompletor setStrict false
+ if (completion ne NoCompletion) {
+ val argCompletor: ArgumentCompleter =
+ new ArgumentCompleter(new JLineDelimiter, scalaToJline(completion.completer()))
+ argCompletor setStrict false
- this addCompleter argCompletor
- this setAutoprintThreshold 400 // max completion candidates without warning
+ this addCompleter argCompletor
+ this setAutoprintThreshold 400 // max completion candidates without warning
+ }
}
}
- val consoleReader: JLineConsoleReader = new JLineConsoleReader()
-
- def currentLine: String = consoleReader.getCursorBuffer.buffer.toString
+ def currentLine = consoleReader.getCursorBuffer.buffer.toString
def redrawLine() = consoleReader.redrawLineAndFlush()
- def eraseLine() = {
- while (consoleReader.delete()) { }
- // consoleReader.eraseLine()
- }
+ def eraseLine() = consoleReader.eraseLine()
+ // Alternate implementation, not sure if/when I need this.
+ // def eraseLine() = while (consoleReader.delete()) { }
def readOneLine(prompt: String) = consoleReader readLine prompt
def readOneKey(prompt: String) = consoleReader readOneKey prompt
}
-
-object JLineReader {
- def apply(intp: IMain): JLineReader = apply(new JLineCompletion(intp))
- def apply(comp: Completion): JLineReader = new JLineReader(comp)
-}
diff --git a/src/compiler/scala/tools/nsc/interpreter/Logger.scala b/src/compiler/scala/tools/nsc/interpreter/Logger.scala
new file mode 100644
index 0000000000..d6cba5da6a
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/interpreter/Logger.scala
@@ -0,0 +1,18 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2011 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala.tools.nsc
+package interpreter
+
+trait Logger {
+ def isInfo: Boolean
+ def isDebug: Boolean
+ def isTrace: Boolean
+ def out: JPrintWriter
+
+ def info(msg: => Any): Unit = if (isInfo) out println msg
+ def debug(msg: => Any): Unit = if (isDebug) out println msg
+ def trace(msg: => Any): Unit = if (isTrace) out println msg
+}
diff --git a/src/compiler/scala/tools/nsc/interpreter/LoopCommands.scala b/src/compiler/scala/tools/nsc/interpreter/LoopCommands.scala
index 196d8b882f..188f891054 100644
--- a/src/compiler/scala/tools/nsc/interpreter/LoopCommands.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/LoopCommands.scala
@@ -27,7 +27,7 @@ object ProcessResult {
}
trait LoopCommands {
- protected def out: java.io.PrintWriter
+ protected def out: JPrintWriter
// a single interpreter command
abstract class LoopCommand(val name: String, val help: String) extends (String => Result) {
diff --git a/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala b/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala
index 330294f823..0d51c16e53 100644
--- a/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala
@@ -9,7 +9,7 @@ package interpreter
import scala.collection.{ mutable, immutable }
import scala.PartialFunction.cond
import scala.reflect.NameTransformer
-import util.Chars
+import scala.tools.nsc.util.Chars
trait MemberHandlers {
val intp: IMain
@@ -52,15 +52,15 @@ trait MemberHandlers {
}
def chooseHandler(member: Tree): MemberHandler = member match {
- case member: DefDef => new DefHandler(member)
- case member: ValDef => new ValHandler(member)
- case member@Assign(Ident(_), _) => new AssignHandler(member)
- case member: ModuleDef => new ModuleHandler(member)
- case member: ClassDef => new ClassHandler(member)
- case member: TypeDef => new TypeAliasHandler(member)
- case member: Import => new ImportHandler(member)
- case DocDef(_, documented) => chooseHandler(documented)
- case member => new GenericHandler(member)
+ case member: DefDef => new DefHandler(member)
+ case member: ValDef => new ValHandler(member)
+ case member: Assign => new AssignHandler(member)
+ case member: ModuleDef => new ModuleHandler(member)
+ case member: ClassDef => new ClassHandler(member)
+ case member: TypeDef => new TypeAliasHandler(member)
+ case member: Import => new ImportHandler(member)
+ case DocDef(_, documented) => chooseHandler(documented)
+ case member => new GenericHandler(member)
}
sealed abstract class MemberDefHandler(override val member: MemberDef) extends MemberHandler(member) {
@@ -80,10 +80,7 @@ trait MemberHandlers {
sealed abstract class MemberHandler(val member: Tree) {
def definesImplicit = false
def definesValue = false
- def isLegalTopLevel = member match {
- case _: ModuleDef | _: ClassDef | _: Import => true
- case _ => false
- }
+ def isLegalTopLevel = false
def definesTerm = Option.empty[TermName]
def definesType = Option.empty[TypeName]
@@ -129,7 +126,7 @@ trait MemberHandlers {
}
class AssignHandler(member: Assign) extends MemberHandler(member) {
- val lhs = member.lhs.asInstanceOf[Ident] // an unfortunate limitation
+ val Assign(lhs, rhs) = member
val name = newTermName(freshInternalVarName())
override def definesTerm = Some(name)
@@ -140,7 +137,7 @@ trait MemberHandlers {
/** Print out lhs instead of the generated varName */
override def resultExtractionCode(req: Request) = {
val lhsType = string2code(req lookupTypeOf name)
- val res = string2code(req fullPath name)
+ val res = string2code(req fullPath name)
""" + "%s: %s = " + %s + "\n" """.format(lhs, lhsType, res) + "\n"
}
@@ -149,6 +146,7 @@ trait MemberHandlers {
class ModuleHandler(module: ModuleDef) extends MemberDefHandler(module) {
override def definesTerm = Some(name)
override def definesValue = true
+ override def isLegalTopLevel = true
override def resultExtractionCode(req: Request) = codegenln("defined module ", name)
}
@@ -156,6 +154,7 @@ trait MemberHandlers {
class ClassHandler(member: ClassDef) extends MemberDefHandler(member) {
override def definesType = Some(name.toTypeName)
override def definesTerm = Some(name.toTermName) filter (_ => mods.isCase)
+ override def isLegalTopLevel = true
override def resultExtractionCode(req: Request) =
codegenln("defined %s %s".format(keyword, name))
@@ -172,6 +171,19 @@ trait MemberHandlers {
class ImportHandler(imp: Import) extends MemberHandler(imp) {
val Import(expr, selectors) = imp
def targetType = intp.typeOfExpression("" + expr)
+ override def isLegalTopLevel = true
+
+ def createImportForName(name: Name): String = {
+ selectors foreach {
+ case sel @ ImportSelector(old, _, `name`, _) => return "import %s.{ %s }".format(expr, sel)
+ case _ => ()
+ }
+ "import %s.%s".format(expr, name)
+ }
+ // TODO: Need to track these specially to honor Predef masking attempts,
+ // because they must be the leading imports in the code generated for each
+ // line. We can use the same machinery as Contexts now, anyway.
+ def isPredefImport = false // treeInfo.isPredefExpr(expr)
// wildcard imports, e.g. import foo._
private def selectorWild = selectors filter (_.name == nme.USCOREkw)
@@ -181,6 +193,9 @@ trait MemberHandlers {
/** Whether this import includes a wildcard import */
val importsWildcard = selectorWild.nonEmpty
+ /** Whether anything imported is implicit .*/
+ def importsImplicit = implicitSymbols.nonEmpty
+
def implicitSymbols = importedSymbols filter (_.isImplicit)
def importedSymbols = individualSymbols ++ wildcardSymbols
diff --git a/src/compiler/scala/tools/nsc/interpreter/Naming.scala b/src/compiler/scala/tools/nsc/interpreter/Naming.scala
index 2b9c8a954a..89868abfb5 100644
--- a/src/compiler/scala/tools/nsc/interpreter/Naming.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/Naming.scala
@@ -6,13 +6,56 @@
package scala.tools.nsc
package interpreter
+/** This is for name logic which is independent of the compiler (notice there's no Global.)
+ * That includes at least generating, metaquoting, mangling, and unmangling.
+ */
trait Naming {
- val global: Global
+ def unmangle(str: String): String = {
+ val cleaned = removeIWPackages(removeLineWrapper(str))
+ var ctrlChars = 0
+ cleaned map { ch =>
+ if (ch.isControl && !ch.isWhitespace) {
+ ctrlChars += 1
+ if (ctrlChars > 5) return "[line elided for control chars: possibly a scala signature]"
+ else '?'
+ }
+ else ch
+ }
+ }
+
+ // The two name forms this is catching are the two sides of this assignment:
+ //
+ // $line3.$read.$iw.$iw.Bippy =
+ // $line3.$read$$iw$$iw$Bippy@4a6a00ca
+
+ private def noMeta(s: String) = "\\Q" + s + "\\E"
+ private lazy val lineRegex = {
+ val sn = sessionNames
+ val members = List(sn.read, sn.eval, sn.print) map noMeta mkString ("(?:", "|", ")")
+ debugging("lineRegex")(noMeta(sn.line) + """\d+[./]""" + members + """[$.]""")
+ }
+
+ private def removeLineWrapper(s: String) = s.replaceAll(lineRegex, "")
+ private def removeIWPackages(s: String) = s.replaceAll("""\$iw[$.]""", "")
+
+ trait SessionNames {
+ // All values are configurable by passing e.g. -Dscala.repl.naming.read=XXX
+ final def propOr(name: String): String = propOr(name, "$" + name)
+ final def propOr(name: String, default: String): String =
+ sys.props.getOrElse("scala.repl.naming." + name, default)
+
+ // Prefixes used in repl machinery. Default to $line, $read, etc.
+ def line = propOr("line")
+ def read = propOr("read")
+ def eval = propOr("eval")
+ def print = propOr("print")
- import global.{ Name, nme }
- import nme.{
- INTERPRETER_VAR_PREFIX, INTERPRETER_SYNTHVAR_PREFIX, INTERPRETER_LINE_PREFIX
+ // The prefix for unnamed results: by default res0, res1, etc.
+ def res = propOr("res", "res") // INTERPRETER_VAR_PREFIX
+ // Internal ones
+ def ires = propOr("ires")
}
+ lazy val sessionNames: SessionNames = new SessionNames { }
/** Generates names pre0, pre1, etc. via calls to apply method */
class NameCreator(pre: String) {
@@ -29,24 +72,21 @@ trait Naming {
(name startsWith pre) && ((name drop pre.length) forall (_.isDigit))
}
- private lazy val line = new NameCreator(INTERPRETER_LINE_PREFIX) // line name, like line$0
- private lazy val userVar = new NameCreator(INTERPRETER_VAR_PREFIX) // var name, like res0
- private lazy val internalVar = new NameCreator(INTERPRETER_SYNTHVAR_PREFIX) // internal var name, like $synthvar0
+ private lazy val userVar = new NameCreator(sessionNames.res) // var name, like res0
+ private lazy val internalVar = new NameCreator(sessionNames.ires) // internal var name, like $ires0
- def isUserVarName(name: String) = userVar didGenerate name
- def isInternalVarName(name: String): Boolean = internalVar didGenerate name
- def isInternalVarName(name: Name): Boolean = internalVar didGenerate name.toString
+ def isLineName(name: String) = (name startsWith sessionNames.line) && (name stripPrefix sessionNames.line forall (_.isDigit))
+ def isUserVarName(name: String) = userVar didGenerate name
+ def isInternalVarName(name: String) = internalVar didGenerate name
val freshLineId = {
var x = 0
() => { x += 1 ; x }
}
- def freshLineName() = line()
def freshUserVarName() = userVar()
def freshInternalVarName() = internalVar()
def resetAllCreators() {
- line.reset()
userVar.reset()
internalVar.reset()
}
diff --git a/src/compiler/scala/tools/nsc/interpreter/Power.scala b/src/compiler/scala/tools/nsc/interpreter/Power.scala
index 5a0830841d..d907d5024f 100644
--- a/src/compiler/scala/tools/nsc/interpreter/Power.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/Power.scala
@@ -56,7 +56,12 @@ abstract class Power[G <: Global](
val intp: IMain
) extends SharesGlobal[G] {
import intp.{ beQuietDuring, interpret, parse }
- import global.{ opt, definitions, stringToTermName, NoSymbol, NoType, analyzer, CompilationUnit }
+ import global.{
+ opt, definitions, analyzer,
+ stringToTermName, typeRef,
+ CompilationUnit,
+ NoSymbol, NoPrefix, NoType
+ }
abstract class SymSlurper {
def isKeep(sym: Symbol): Boolean
@@ -132,12 +137,14 @@ abstract class Power[G <: Global](
|** New defs! Type power.<tab> to reveal **
""".stripMargin.trim
- def init = customInit getOrElse """
- |import scala.tools.nsc._
- |import scala.collection.JavaConverters._
- |import global.{ error => _, _ }
- |import power.Implicits._
- """.stripMargin
+ private def initImports = List(
+ "scala.tools.nsc._",
+ "scala.collection.JavaConverters._",
+ "global.{ error => _, _ }",
+ "power.Implicits._",
+ "power.rutil._"
+ )
+ def init = customInit getOrElse "import " + initImports.mkString(", ")
/** Starts up power mode and runs whatever is in init.
*/
@@ -205,7 +212,7 @@ abstract class Power[G <: Global](
}
def ? = this
- def whoHas(name: String) = bts filter (_.decls.toList exists (_.name.toString == name))
+ def whoHas(name: String) = bts filter (_.decls exists (_.name.toString == name))
def <:<[U: Manifest](other: U) = tpe <:< InternalInfo[U].tpe
def lub[U: Manifest](other: U) = global.lub(List(tpe, InternalInfo[U].tpe))
def glb[U: Manifest](other: U) = global.glb(List(tpe, InternalInfo[U].tpe))
@@ -219,85 +226,97 @@ abstract class Power[G <: Global](
trait LowPriorityPrettifier {
implicit object AnyPrettifier extends Prettifier[Any] {
- def prettify(x: Any): List[String] = x match {
+ def show(x: Any): Unit = prettify(x) foreach println
+ def prettify(x: Any): TraversableOnce[String] = x match {
case x: Name => List(x.decode)
- case Tuple2(k, v) => List(prettify(k) ++ Seq("->") ++ prettify(v) mkString " ")
- case xs: TraversableOnce[_] => (xs.toList flatMap prettify).sorted
- case x => List(rutil.stringOf(x))
+ case Tuple2(k, v) => List(prettify(k).toIterator ++ Iterator("->") ++ prettify(v) mkString " ")
+ case xs: Array[_] => xs.iterator flatMap prettify
+ case xs: TraversableOnce[_] => xs flatMap prettify
+ case x => List(Prettifier.stringOf(x))
}
}
}
+ object StringPrettifier extends Prettifier[String] {
+ def show(x: String) = println(x)
+ def prettify(x: String) = List(Prettifier stringOf x)
+ }
object Prettifier extends LowPriorityPrettifier {
- def prettify[T](value: T): List[String] = default[T] prettify value
+ def stringOf(x: Any): String = scala.runtime.ScalaRunTime.stringOf(x)
+ def prettify[T](value: T): TraversableOnce[String] = default[T] prettify value
def default[T] = new Prettifier[T] {
- def prettify(x: T): List[String] = AnyPrettifier prettify x
+ def prettify(x: T): TraversableOnce[String] = AnyPrettifier prettify x
+ def show(x: T): Unit = AnyPrettifier show x
}
}
trait Prettifier[T] {
- def prettify(x: T): List[String]
-
- private var indentLevel = 0
- private def spaces = " " * indentLevel
- def indented[T](body: => T): T = {
- indentLevel += 1
- try body
- finally indentLevel -= 1
- }
+ def show(x: T): Unit
+ def prettify(x: T): TraversableOnce[String]
- def show(x: T): Unit = grep(x, _ => true)
- def grep(x: T, p: String => Boolean): Unit =
- prettify(x) filter p foreach (x => println(spaces + x))
+ def show(xs: TraversableOnce[T]): Unit = prettify(xs) foreach println
+ def prettify(xs: TraversableOnce[T]): TraversableOnce[String] = xs flatMap (x => prettify(x))
}
- class MultiPrintingConvenience[T: Prettifier](coll: TraversableOnce[T]) {
+
+ abstract class PrettifierClass[T: Prettifier]() {
val pretty = implicitly[Prettifier[T]]
import pretty._
- def freqBy[U](p: T => U) = {
- val map = coll.toList groupBy p
- map.toList sortBy (-_._2.size)
- }
- def freqByFormatted[U](p: T => U) = {
- val buf = new mutable.ListBuffer[String]
+ def value: Seq[T]
- freqBy(p) foreach { case (k, vs) =>
- buf += "%d: %s".format(vs.size, Prettifier.prettify(k))
- vs flatMap prettify foreach (buf += " " + _)
- }
- buf.toList
- }
+ def pp(f: Seq[T] => Seq[T]): Unit =
+ pretty prettify f(value) foreach (StringPrettifier show _)
+
+ def freq[U](p: T => U) = (value.toSeq groupBy p mapValues (_.size)).toList sortBy (-_._2) map (_.swap)
+ def ppfreq[U](p: T => U): Unit = freq(p) foreach { case (count, key) => println("%5d %s".format(count, key)) }
+
+ def |[U](f: Seq[T] => Seq[U]): Seq[U] = f(value)
+ def ^^[U](f: T => U): Seq[U] = value map f
+ def ^?[U](pf: PartialFunction[T, U]): Seq[U] = value collect pf
+
+ def >>!(implicit ord: Ordering[T]): Unit = pp(_.sorted.distinct)
+ def >>(implicit ord: Ordering[T]): Unit = pp(_.sorted)
+ def >!(): Unit = pp(_.distinct)
+ def >(): Unit = pp(identity)
- /** It makes sense.
- *
- * # means how many
- * ? means "I said, HOW MANY?"
- * > means print
- *
- * Now don't you feel silly for what you were thinking.
- */
- def #?>[U](p: T => U) = this freqByFormatted p foreach println
- def #?[U](p: T => U) = this freqByFormatted p
+ def >#(): Unit = this ># (identity[T] _)
+ def >#[U](p: T => U): Unit = this ppfreq p
+
+ def >?(p: T => Boolean): Unit = pp(_ filter p)
+ def >?(s: String): Unit = pp(_ filter (_.toString contains s))
+ def >?(r: Regex): Unit = pp(_ filter (_.toString matches fixRegex(r)))
+
+ private def fixRegex(r: scala.util.matching.Regex): String = {
+ val s = r.pattern.toString
+ val prefix = if (s startsWith "^") "" else """^.*?"""
+ val suffix = if (s endsWith "$") "" else """.*$"""
+
+ prefix + s + suffix
+ }
}
- class PrintingConvenience[T: Prettifier](value: T) {
- val pretty = implicitly[Prettifier[T]]
+ class MultiPrettifierClass[T: Prettifier](val value: Seq[T]) extends PrettifierClass[T]() { }
+ class SinglePrettifierClass[T: Prettifier](single: T) extends PrettifierClass[T]() {
+ val value = List(single)
+ }
- def >() { >(_ => true) }
- def >(s: String): Unit = >(_ contains s)
- def >(r: Regex): Unit = >(_ matches r.pattern.toString)
- def >(p: String => Boolean): Unit = pretty.grep(value, p)
+ class RichReplString(s: String) {
+ def u: URL = (
+ if (s contains ":") new java.net.URL(s)
+ else if (new java.io.File(s) exists) new java.io.File(s).toURI.toURL
+ else new java.net.URL("http://" + s)
+ )
}
class RichInputStream(in: InputStream)(implicit codec: Codec) {
- def bytes(): Array[Byte] = io.Streamable.bytes(in)
- def slurp(): String = io.Streamable.slurp(in)
+ def bytes(): Array[Byte] = io.Streamable.bytes(in)
+ def slurp(): String = io.Streamable.slurp(in)
+ def <<(): String = slurp()
}
protected trait Implicits1 {
// fallback
- implicit def replPrinting[T](x: T)(implicit pretty: Prettifier[T] = Prettifier.default[T]) = new PrintingConvenience[T](x)
+ implicit def replPrinting[T](x: T)(implicit pretty: Prettifier[T] = Prettifier.default[T]) =
+ new SinglePrettifierClass[T](x)
}
- trait Implicits2 extends Implicits1 with SharesGlobal[G] {
- import global._
-
+ trait Implicits2 extends Implicits1 {
class RichSymbol(sym: Symbol) {
// convenient type application
def apply(targs: Type*): Type = typeRef(NoPrefix, sym, targs.toList)
@@ -312,18 +331,19 @@ abstract class Power[G <: Global](
implicit lazy val powerSymbolOrdering: Ordering[Symbol] = Ordering[Name] on (_.name)
implicit lazy val powerTypeOrdering: Ordering[Type] = Ordering[Symbol] on (_.typeSymbol)
- implicit def replCollPrinting[T: Prettifier](xs: TraversableOnce[T]): MultiPrintingConvenience[T] = new MultiPrintingConvenience[T](xs)
+ implicit def replEnhancedStrings(s: String): RichReplString = new RichReplString(s)
+ implicit def replMultiPrinting[T: Prettifier](xs: TraversableOnce[T]): MultiPrettifierClass[T] =
+ new MultiPrettifierClass[T](xs.toSeq)
implicit def replInternalInfo[T: Manifest](x: T): InternalInfo[T] = new InternalInfo[T](Some(x))
implicit def replPrettifier[T] : Prettifier[T] = Prettifier.default[T]
implicit def replTypeApplication(sym: Symbol): RichSymbol = new RichSymbol(sym)
- implicit def replInputStream(in: InputStream)(implicit codec: Codec): RichInputStream = new RichInputStream(in)
- implicit def replInputStreamURL(url: URL)(implicit codec: Codec) = replInputStream(url.openStream())
- }
- object Implicits extends Implicits2 {
- val global: G = Power.this.global
+ implicit def replInputStream(in: InputStream)(implicit codec: Codec) = new RichInputStream(in)
+ implicit def replInputStreamURL(url: URL)(implicit codec: Codec) = new RichInputStream(url.openStream())
}
+ object Implicits extends Implicits2 { }
trait ReplUtilities {
+ def info[T: Manifest] = InternalInfo[T]
def ?[T: Manifest] = InternalInfo[T]
def url(s: String) = {
try new URL(s)
@@ -344,7 +364,6 @@ abstract class Power[G <: Global](
case (next, rest) => next.map(_.toChar).mkString :: strings(rest)
}
}
- def stringOf(x: Any): String = scala.runtime.ScalaRunTime.stringOf(x)
}
lazy val rutil: ReplUtilities = new ReplUtilities { }
diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplConfig.scala b/src/compiler/scala/tools/nsc/interpreter/ReplConfig.scala
index a8287e3a9a..c4a77e9630 100644
--- a/src/compiler/scala/tools/nsc/interpreter/ReplConfig.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/ReplConfig.scala
@@ -6,37 +6,30 @@
package scala.tools.nsc
package interpreter
-import sys.{ Prop, BooleanProp }
-
trait ReplConfig {
-
- class ReplProps {
- private def bool(name: String) = BooleanProp.keyExists(name)
-
- val jlineDebug = bool("scala.tools.jline.internal.Log.debug")
- val jlineTrace = bool("scala.tools.jline.internal.Log.trace")
-
- val debug = bool("scala.repl.debug")
- val trace = bool("scala.repl.trace")
- val power = bool("scala.repl.power")
-
- val replInitCode = Prop[JFile]("scala.repl.initcode")
- val powerInitCode = Prop[JFile]("scala.repl.power.initcode")
- val powerBanner = Prop[JFile]("scala.repl.power.banner")
- }
lazy val replProps = new ReplProps
- /** Debug output */
- private[nsc] def repldbg(msg: String) = if (isReplDebug) Console println msg
+ class TapMaker[T](x: T) {
+ def tapInfo(msg: => String): T = tap(x => replinfo(parens(x)))
+ def tapDebug(msg: => String): T = tap(x => repldbg(parens(x)))
+ def tapTrace(msg: => String): T = tap(x => repltrace(parens(x)))
+ def tap[U](f: T => U): T = {
+ f(x)
+ x
+ }
+ }
- /** Tracing */
- private[nsc] def tracing[T](msg: String)(x: T): T = {
- if (isReplDebug)
- println("(" + msg + ") " + x)
+ private def parens(x: Any) = "(" + x + ")"
+ private def echo(msg: => String) =
+ try Console println msg
+ catch { case x: AssertionError => Console.println("Assertion error printing debugging output: " + x) }
- x
- }
+ private[nsc] def repldbg(msg: => String) = if (isReplDebug) echo(msg)
+ private[nsc] def repltrace(msg: => String) = if (isReplTrace) echo(msg)
+ private[nsc] def replinfo(msg: => String) = if (isReplInfo) echo(msg)
- def isReplDebug: Boolean = replProps.debug
+ def isReplTrace: Boolean = replProps.trace
+ def isReplDebug: Boolean = replProps.debug || isReplTrace
+ def isReplInfo: Boolean = replProps.info || isReplDebug
def isReplPower: Boolean = replProps.power
}
diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplProps.scala b/src/compiler/scala/tools/nsc/interpreter/ReplProps.scala
new file mode 100644
index 0000000000..5eb1e0ae18
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/interpreter/ReplProps.scala
@@ -0,0 +1,25 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2011 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala.tools.nsc
+package interpreter
+
+import scala.sys._
+
+class ReplProps {
+ private def bool(name: String) = BooleanProp.keyExists(name)
+
+ val jlineDebug = bool("scala.tools.jline.internal.Log.debug")
+ val jlineTrace = bool("scala.tools.jline.internal.Log.trace")
+
+ val info = bool("scala.repl.info")
+ val debug = bool("scala.repl.debug")
+ val trace = bool("scala.repl.trace")
+ val power = bool("scala.repl.power")
+
+ val replInitCode = Prop[JFile]("scala.repl.initcode")
+ val powerInitCode = Prop[JFile]("scala.repl.power.initcode")
+ val powerBanner = Prop[JFile]("scala.repl.power.banner")
+}
diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplReporter.scala b/src/compiler/scala/tools/nsc/interpreter/ReplReporter.scala
new file mode 100644
index 0000000000..e181f98018
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/interpreter/ReplReporter.scala
@@ -0,0 +1,22 @@
+/* NSC -- new Scala compiler
+ * Copyright 2002-2011 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala.tools.nsc
+package interpreter
+
+import reporters._
+import IMain._
+
+class ReplReporter(intp: IMain) extends ConsoleReporter(intp.settings, null, new ReplStrippingWriter(intp)) {
+ override def printMessage(msg: String) {
+ // Avoiding deadlock if the compiler starts logging before
+ // the lazy val is complete.
+ if (intp.isInitializeComplete) {
+ if (intp.totalSilence) ()
+ else super.printMessage(msg)
+ }
+ else Console.println("[init] " + msg)
+ }
+}
diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplStrings.scala b/src/compiler/scala/tools/nsc/interpreter/ReplStrings.scala
index cef46c963a..9c83db3d85 100644
--- a/src/compiler/scala/tools/nsc/interpreter/ReplStrings.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/ReplStrings.scala
@@ -9,7 +9,7 @@ package interpreter
import scala.collection.{ mutable, immutable }
import scala.PartialFunction.cond
import scala.reflect.NameTransformer
-import util.Chars
+import scala.tools.nsc.util.Chars
trait ReplStrings {
// Longest common prefix
diff --git a/src/compiler/scala/tools/nsc/interpreter/SimpleReader.scala b/src/compiler/scala/tools/nsc/interpreter/SimpleReader.scala
index 5249f89dfb..992bef8056 100644
--- a/src/compiler/scala/tools/nsc/interpreter/SimpleReader.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/SimpleReader.scala
@@ -6,13 +6,13 @@
package scala.tools.nsc
package interpreter
-import java.io.{ BufferedReader, PrintWriter }
+import java.io.{ BufferedReader }
import session.NoHistory
/** Reads using standard JDK API */
class SimpleReader(
in: BufferedReader,
- out: PrintWriter,
+ out: JPrintWriter,
val interactive: Boolean)
extends InteractiveReader
{
@@ -37,8 +37,8 @@ extends InteractiveReader
object SimpleReader {
def defaultIn = Console.in
- def defaultOut = new PrintWriter(Console.out)
+ def defaultOut = new JPrintWriter(Console.out)
- def apply(in: BufferedReader = defaultIn, out: PrintWriter = defaultOut, interactive: Boolean = true): SimpleReader =
+ def apply(in: BufferedReader = defaultIn, out: JPrintWriter = defaultOut, interactive: Boolean = true): SimpleReader =
new SimpleReader(in, out, interactive)
} \ No newline at end of file
diff --git a/src/compiler/scala/tools/nsc/interpreter/package.scala b/src/compiler/scala/tools/nsc/interpreter/package.scala
index 1865e90e92..e78e92c8f8 100644
--- a/src/compiler/scala/tools/nsc/interpreter/package.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/package.scala
@@ -27,12 +27,19 @@ package object interpreter extends ReplConfig with ReplStrings {
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
- private[nsc] implicit def enrichClass[T](clazz: Class[T]) = new RichClass[T](clazz)
+ val IR = Results
+
private[interpreter] implicit def javaCharSeqCollectionToScala(xs: JCollection[_ <: CharSequence]): List[String] = {
import 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 tracing[T](msg: String)(x: T): T = x.tapTrace(msg)
+ private[nsc] def debugging[T](msg: String)(x: T) = x.tapDebug(msg)
}
diff --git a/src/compiler/scala/tools/nsc/interpreter/session/FileBackedHistory.scala b/src/compiler/scala/tools/nsc/interpreter/session/FileBackedHistory.scala
index 5f11a0ea8f..e238bdf654 100644
--- a/src/compiler/scala/tools/nsc/interpreter/session/FileBackedHistory.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/session/FileBackedHistory.scala
@@ -46,9 +46,17 @@ trait FileBackedHistory extends JLineHistory with JPersistentHistory {
if (!historyFile.canRead)
historyFile.createFile()
- val lines: IndexedSeq[String] =
+ val lines: IndexedSeq[String] = {
try historyFile.lines().toIndexedSeq
- catch { case _: Exception => Vector() }
+ catch {
+ // It seems that control characters in the history file combined
+ // with the default codec can lead to nio spewing exceptions. Rather
+ // than abandon hope we'll try to read it as ISO-8859-1
+ case _: Exception =>
+ try historyFile.lines("ISO-8859-1").toIndexedSeq
+ catch { case _: Exception => Vector() }
+ }
+ }
repldbg("Loading " + lines.size + " into history.")
@@ -61,6 +69,7 @@ trait FileBackedHistory extends JLineHistory with JPersistentHistory {
}
moveToEnd()
}
+
def flush(): Unit = ()
def purge(): Unit = historyFile.truncate()
}
diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
index a92b5d7034..ce1236d788 100644
--- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
@@ -143,6 +143,7 @@ trait ScalaSettings extends AbsScalaSettings with StandardScalaSettings {
BooleanSetting ("-Ybuild-manager-debug", "Generate debug information for the Refined Build Manager compiler.")
val Ytyperdebug = BooleanSetting ("-Ytyper-debug", "Trace all type assignments.")
val Ypmatdebug = BooleanSetting ("-Ypmat-debug", "Trace all pattern matcher activity.")
+ val Yreplsync = BooleanSetting ("-Yrepl-sync", "Do not use asynchronous code for repl startup")
val Yrepldebug = BooleanSetting ("-Yrepl-debug", "Trace all repl activity.") .
withPostSetHook(_ => interpreter.replProps.debug setValue true)
val Ycompletion = BooleanSetting ("-Ycompletion-debug", "Trace all tab completion activity.")
diff --git a/src/partest/scala/tools/partest/ReplTest.scala b/src/partest/scala/tools/partest/ReplTest.scala
index b31c43ba6f..02ab154d4b 100644
--- a/src/partest/scala/tools/partest/ReplTest.scala
+++ b/src/partest/scala/tools/partest/ReplTest.scala
@@ -14,48 +14,26 @@ import java.lang.reflect.{ Method => JMethod, Field => JField }
*/
abstract class ReplTest extends App {
def code: String
- def settings: Settings = new Settings // override for custom settings
+ // override to add additional settings with strings
+ def extraSettings: String = ""
+ // override to transform Settings object immediately before the finish
+ def transformSettings(s: Settings): Settings = s
+
+ // final because we need to enforce the existence of a couple settings.
+ final def settings: Settings = {
+ val s = new Settings
+ s.Yreplsync.value = true
+ s.Xnojline.value = true
+ val settingString = sys.props("scala.partest.debug.repl-args") match {
+ case null => extraSettings
+ case s => extraSettings + " " + s
+ }
+ s processArgumentString settingString
+ transformSettings(s)
+ }
def eval() = ILoop.runForTranscript(code, settings).lines drop 1
def show() = eval() foreach println
- show()
-}
-
-trait SigTest {
- def mstr(m: JMethod) = " (m) %s%s".format(
- m.toGenericString,
- if (m.isBridge) " (bridge)" else ""
- )
- def fstr(f: JField) = " (f) %s".format(f.toGenericString)
-
- def isObjectMethodName(name: String) = classOf[Object].getMethods exists (_.getName == name)
-
- def fields[T: ClassManifest](p: JField => Boolean) = {
- val cl = classManifest[T].erasure
- val fs = (cl.getFields ++ cl.getDeclaredFields).distinct sortBy (_.getName)
-
- fs filter p
- }
- def methods[T: ClassManifest](p: JMethod => Boolean) = {
- val cl = classManifest[T].erasure
- val ms = (cl.getMethods ++ cl.getDeclaredMethods).distinct sortBy (x => (x.getName, x.isBridge))
-
- ms filter p
- }
- def allFields[T: ClassManifest]() = fields[T](_ => true)
- def allMethods[T: ClassManifest]() = methods[T](m => !isObjectMethodName(m.getName))
- def fieldsNamed[T: ClassManifest](name: String) = fields[T](_.getName == name)
- def methodsNamed[T: ClassManifest](name: String) = methods[T](_.getName == name)
-
- def allGenericStrings[T: ClassManifest]() =
- (allMethods[T]() map mstr) ++ (allFields[T]() map fstr)
-
- def genericStrings[T: ClassManifest](name: String) =
- (methodsNamed[T](name) map mstr) ++ (fieldsNamed[T](name) map fstr)
-
- def show[T: ClassManifest](name: String = "") = {
- println(classManifest[T].erasure.getName)
- if (name == "") allGenericStrings[T]() foreach println
- else genericStrings[T](name) foreach println
- }
+ try show()
+ catch { case t => println(t) ; sys.exit(1) }
}
diff --git a/src/partest/scala/tools/partest/SigTest.scala b/src/partest/scala/tools/partest/SigTest.scala
new file mode 100644
index 0000000000..072ec006f9
--- /dev/null
+++ b/src/partest/scala/tools/partest/SigTest.scala
@@ -0,0 +1,51 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2011 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala.tools.partest
+
+import scala.tools.nsc.Settings
+import scala.tools.nsc.interpreter.ILoop
+import java.lang.reflect.{ Method => JMethod, Field => JField }
+
+/** Support code for testing signatures.
+ */
+trait SigTest {
+ def mstr(m: JMethod) = " (m) %s%s".format(
+ m.toGenericString,
+ if (m.isBridge) " (bridge)" else ""
+ )
+ def fstr(f: JField) = " (f) %s".format(f.toGenericString)
+
+ def isObjectMethodName(name: String) = classOf[Object].getMethods exists (_.getName == name)
+
+ def fields[T: ClassManifest](p: JField => Boolean) = {
+ val cl = classManifest[T].erasure
+ val fs = (cl.getFields ++ cl.getDeclaredFields).distinct sortBy (_.getName)
+
+ fs filter p
+ }
+ def methods[T: ClassManifest](p: JMethod => Boolean) = {
+ val cl = classManifest[T].erasure
+ val ms = (cl.getMethods ++ cl.getDeclaredMethods).distinct sortBy (x => (x.getName, x.isBridge))
+
+ ms filter p
+ }
+ def allFields[T: ClassManifest]() = fields[T](_ => true)
+ def allMethods[T: ClassManifest]() = methods[T](m => !isObjectMethodName(m.getName))
+ def fieldsNamed[T: ClassManifest](name: String) = fields[T](_.getName == name)
+ def methodsNamed[T: ClassManifest](name: String) = methods[T](_.getName == name)
+
+ def allGenericStrings[T: ClassManifest]() =
+ (allMethods[T]() map mstr) ++ (allFields[T]() map fstr)
+
+ def genericStrings[T: ClassManifest](name: String) =
+ (methodsNamed[T](name) map mstr) ++ (fieldsNamed[T](name) map fstr)
+
+ def show[T: ClassManifest](name: String = "") = {
+ println(classManifest[T].erasure.getName)
+ if (name == "") allGenericStrings[T]() foreach println
+ else genericStrings[T](name) foreach println
+ }
+}
diff --git a/test/files/jvm/interpreter.check b/test/files/jvm/interpreter.check
index dd0f39551c..42a8ae8855 100644
--- a/test/files/jvm/interpreter.check
+++ b/test/files/jvm/interpreter.check
@@ -13,7 +13,7 @@ scala> def gcd(x: Int, y: Int): Int = {
else if (y == 0) x
else gcd(y%x, x)
}
-gcd: (x: Int,y: Int)Int
+gcd: (x: Int, y: Int)Int
scala> val five = gcd(15,35)
five: Int = 5
@@ -370,5 +370,5 @@ plusOne: (x: Int)Int
res0: Int = 6
res1: java.lang.String = after reset
<console>:8: error: not found: value plusOne
- plusOne(5) // should be undefined now
- ^
+ plusOne(5) // should be undefined now
+ ^
diff --git a/test/files/jvm/interpreter.scala b/test/files/jvm/interpreter.scala
new file mode 100644
index 0000000000..752a129950
--- /dev/null
+++ b/test/files/jvm/interpreter.scala
@@ -0,0 +1,158 @@
+import scala.tools.nsc._
+import scala.tools.partest.ReplTest
+
+object Test extends ReplTest {
+ def code = <code>
+// basics
+3+4
+def gcd(x: Int, y: Int): Int = {{
+ if (x == 0) y
+ else if (y == 0) x
+ else gcd(y%x, x)
+}}
+val five = gcd(15,35)
+var x = 1
+x = 2
+val three = x+1
+type anotherint = Int
+val four: anotherint = 4
+val bogus: anotherint = "hello"
+trait PointlessTrait
+val (x,y) = (2,3)
+println("hello")
+
+// ticket #1513
+val t1513 = Array(null)
+// ambiguous toString problem from #547
+val atom = new scala.xml.Atom()
+// overriding toString problem from #1404
+class S(override val toString : String)
+val fish = new S("fish")
+// Test that arrays pretty print nicely.
+val arr = Array("What's", "up", "doc?")
+// Test that arrays pretty print nicely, even when we give them type Any
+val arrInt : Any = Array(1,2,3)
+// Test that nested arrays are pretty-printed correctly
+val arrArrInt : Any = Array(Array(1, 2), Array(3, 4))
+
+// implicit conversions
+case class Foo(n: Int)
+case class Bar(n: Int)
+implicit def foo2bar(foo: Foo) = Bar(foo.n)
+val bar: Bar = Foo(3)
+
+// importing from a previous result
+import bar._
+val m = n
+
+// stressing the imports mechanism
+val one = 1
+val one = 1
+val one = 1
+val one = 1
+val one = 1
+val one = 1
+val one = 1
+val one = 1
+val one = 1
+val one = 1
+val one = 1
+val one = 1
+val one = 1
+val one = 1
+val one = 1
+val one = 1
+val one = 1
+val one = 1
+val one = 1
+val one = 1
+
+
+val x1 = 1
+val x2 = 1
+val x3 = 1
+val x4 = 1
+val x5 = 1
+val x6 = 1
+val x7 = 1
+val x8 = 1
+val x9 = 1
+val x10 = 1
+val x11 = 1
+val x12 = 1
+val x13 = 1
+val x14 = 1
+val x15 = 1
+val x16 = 1
+val x17 = 1
+val x18 = 1
+val x19 = 1
+val x20 = 1
+
+val two = one + x5
+
+// handling generic wildcard arrays (#2386)
+// It's put here because type feedback is an important part of it.
+val xs: Array[_] = Array(1, 2)
+xs.size
+xs.head
+xs filter (_ == 2)
+xs map (_ => "abc")
+xs map (x => x)
+xs map (x => (x, x))
+
+// interior syntax errors should *not* go into multi-line input mode.
+// both of the following should abort immediately:
+def x => y => z
+[1,2,3]
+
+
+// multi-line XML
+&lt;a>
+&lt;b
+ c="c"
+ d="dd"
+/>&lt;/a>
+
+
+/*
+ /*
+ multi-line comment
+ */
+*/
+
+
+// multi-line string
+"""
+hello
+there
+"""
+
+(1 + // give up early by typing two blank lines
+
+
+// defining and using quoted names should work (ticket #323)
+def `match` = 1
+val x = `match`
+
+// multiple classes defined on one line
+sealed class Exp; class Fact extends Exp; class Term extends Exp
+def f(e: Exp) = e match {{ // non-exhaustive warning here
+ case _:Fact => 3
+}}
+
+</code>.text
+
+ def appendix() = {
+ val settings = new Settings
+ settings.classpath.value = sys.props("java.class.path")
+ val interp = new Interpreter(settings)
+ interp.interpret("def plusOne(x: Int) = x + 1")
+ interp.interpret("plusOne(5)")
+ interp.reset()
+ interp.interpret("\"after reset\"")
+ interp.interpret("plusOne(5) // should be undefined now")
+ }
+
+ appendix()
+}
diff --git a/test/files/run/bug4710.check b/test/files/run/bug4710.check
new file mode 100644
index 0000000000..aa2f08d452
--- /dev/null
+++ b/test/files/run/bug4710.check
@@ -0,0 +1,7 @@
+Type in expressions to have them evaluated.
+Type :help for more information.
+
+scala> def method : String = { implicit def f(s: Symbol) = "" ; 'symbol }
+method: String
+
+scala>
diff --git a/test/files/run/bug4710.scala b/test/files/run/bug4710.scala
new file mode 100644
index 0000000000..5e5b1e86b5
--- /dev/null
+++ b/test/files/run/bug4710.scala
@@ -0,0 +1,6 @@
+import scala.tools.partest.ReplTest
+
+object Test extends ReplTest {
+ def code = """def method : String = { implicit def f(s: Symbol) = "" ; 'symbol }"""
+}
+
diff --git a/test/files/run/constrained-types.scala b/test/files/run/constrained-types.scala
index 5f7eb7adde..91bd856d00 100644
--- a/test/files/run/constrained-types.scala
+++ b/test/files/run/constrained-types.scala
@@ -79,15 +79,12 @@ val x : Int @Where(self > 0 && self < 100) = 3
"""
- override def settings: Settings = {
- val s = new Settings
-
+ override def transformSettings(s: Settings): Settings = {
s.Xexperimental.value = true
s.selfInAnnots.value = true
s.deprecation.value = true
// when running that compiler, give it a scala-library to the classpath
s.classpath.value = sys.props("java.class.path")
-
s
}
}
diff --git a/test/files/run/repl-bare-expr.check b/test/files/run/repl-bare-expr.check
new file mode 100644
index 0000000000..04daa48232
--- /dev/null
+++ b/test/files/run/repl-bare-expr.check
@@ -0,0 +1,36 @@
+Type in expressions to have them evaluated.
+Type :help for more information.
+
+scala>
+
+scala> 2 ; 3
+res0: Int = 3
+
+scala> { 2 ; 3 }
+res1: Int = 3
+
+scala> 5 ; 10 ; case object Cow ; 20 ; class Moo { override def toString = "Moooooo" } ; 30 ; def bippy = {
+ 1 +
+ 2 +
+ 3 } ; bippy+88+11
+defined module Cow
+defined class Moo
+bippy: Int
+res2: Int = 105
+
+scala>
+
+scala> object Bovine { var x: List[_] = null } ; case class Ruminant(x: Int) ; bippy * bippy * bippy
+defined module Bovine
+defined class Ruminant
+res3: Int = 216
+
+scala> Bovine.x = List(Ruminant(5), Cow, new Moo)
+Bovine.x: List[Any] = List(Ruminant(5), Cow, Moooooo)
+
+scala> Bovine.x
+res4: List[Any] = List(Ruminant(5), Cow, Moooooo)
+
+scala>
+
+scala>
diff --git a/test/files/run/repl-bare-expr.scala b/test/files/run/repl-bare-expr.scala
new file mode 100644
index 0000000000..df9849fa6d
--- /dev/null
+++ b/test/files/run/repl-bare-expr.scala
@@ -0,0 +1,16 @@
+import scala.tools.partest.ReplTest
+
+object Test extends ReplTest {
+ def code = """
+2 ; 3
+{ 2 ; 3 }
+5 ; 10 ; case object Cow ; 20 ; class Moo { override def toString = "Moooooo" } ; 30 ; def bippy = {
+ 1 +
+ 2 +
+ 3 } ; bippy+88+11
+
+object Bovine { var x: List[_] = null } ; case class Ruminant(x: Int) ; bippy * bippy * bippy
+Bovine.x = List(Ruminant(5), Cow, new Moo)
+Bovine.x
+ """
+} \ No newline at end of file
diff --git a/test/files/run/repl-colon-type.check b/test/files/run/repl-colon-type.check
new file mode 100644
index 0000000000..66c2fcc77f
--- /dev/null
+++ b/test/files/run/repl-colon-type.check
@@ -0,0 +1,57 @@
+Type in expressions to have them evaluated.
+Type :help for more information.
+
+scala>
+
+scala> :type List[1, 2, 3]
+<console>:2: error: identifier expected but integer literal found.
+ List[1, 2, 3]
+ ^
+
+
+scala> :type List(1, 2, 3)
+List[Int]
+
+scala> :type def foo[T](x: T) = List(x)
+[T](x: T)List[T]
+
+scala> :type val bar = List(Set(1))
+List[scala.collection.immutable.Set[Int]]
+
+scala> :type lazy val bar = Set(Set(1))
+scala.collection.immutable.Set[scala.collection.immutable.Set[Int]]
+
+scala> :type def f[T >: Null, U <: String](x: T, y: U) = Set(x, y)
+[T >: Null, U <: String](x: T, y: U)scala.collection.immutable.Set[Any]
+
+scala> :type def x = 1 ; def bar[T >: Null <: AnyRef](xyz: T) = 5
+[T >: Null <: AnyRef](xyz: T)Int
+
+scala>
+
+scala> :type 5
+Int
+
+scala> :type val f = 5
+Int
+
+scala> :type lazy val f = 5
+Int
+
+scala> :type protected lazy val f = 5
+Int
+
+scala> :type def f = 5
+Int
+
+scala> :type def f() = 5
+()Int
+
+scala>
+
+scala> :type def g[T](xs: Set[_ <: T]) = Some(xs.head)
+[T](xs: Set[_ <: T])Some[T]
+
+scala>
+
+scala>
diff --git a/test/files/run/repl-colon-type.scala b/test/files/run/repl-colon-type.scala
new file mode 100644
index 0000000000..39ab580d2a
--- /dev/null
+++ b/test/files/run/repl-colon-type.scala
@@ -0,0 +1,23 @@
+import scala.tools.partest.ReplTest
+
+object Test extends ReplTest {
+ def code = """
+ |:type List[1, 2, 3]
+ |:type List(1, 2, 3)
+ |:type def foo[T](x: T) = List(x)
+ |:type val bar = List(Set(1))
+ |:type lazy val bar = Set(Set(1))
+ |:type def f[T >: Null, U <: String](x: T, y: U) = Set(x, y)
+ |:type def x = 1 ; def bar[T >: Null <: AnyRef](xyz: T) = 5
+ |
+ |:type 5
+ |:type val f = 5
+ |:type lazy val f = 5
+ |:type protected lazy val f = 5
+ |:type def f = 5
+ |:type def f() = 5
+ |
+ |:type def g[T](xs: Set[_ <: T]) = Some(xs.head)
+ """.stripMargin
+}
+
diff --git a/test/files/run/repl-parens.check b/test/files/run/repl-parens.check
new file mode 100644
index 0000000000..f41c2e74bd
--- /dev/null
+++ b/test/files/run/repl-parens.check
@@ -0,0 +1,58 @@
+Type in expressions to have them evaluated.
+Type :help for more information.
+
+scala> (2)
+res0: Int = 2
+
+scala> (2 + 2)
+res1: Int = 4
+
+scala> ((2 + 2))
+res2: Int = 4
+
+scala> ((2 + 2))
+res3: Int = 4
+
+scala> ( (2 + 2))
+res4: Int = 4
+
+scala> ( (2 + 2 ) )
+res5: Int = 4
+
+scala> 5 ; ( (2 + 2 ) ) ; ((5))
+res6: Int = 5
+
+scala> (((2 + 2)), ((2 + 2)))
+res7: (Int, Int) = (4,4)
+
+scala> (((2 + 2)), ((2 + 2)), 2)
+res8: (Int, Int, Int) = (4,4,2)
+
+scala> ((((2 + 2)), ((2 + 2)), 2).productIterator ++ Iterator(3) mkString)
+res9: String = 4423
+
+scala>
+
+scala> 55 ; ((2 + 2)) ; (1, 2, 3)
+res10: (Int, Int, Int) = (1,2,3)
+
+scala>
+
+scala> () => 5
+res11: () => Int = <function0>
+
+scala> 55 ; () => 5
+res12: () => Int = <function0>
+
+scala> () => { class X ; new X }
+res13: () => java.lang.Object with ScalaObject = <function0>
+
+scala>
+
+scala> def foo(x: Int)(y: Int)(z: Int) = x+y+z
+foo: (x: Int)(y: Int)(z: Int)Int
+
+scala> foo(5)(10)(15)+foo(5)(10)(15)
+res14: Int = 60
+
+scala>
diff --git a/test/files/run/repl-parens.scala b/test/files/run/repl-parens.scala
new file mode 100644
index 0000000000..081db3606a
--- /dev/null
+++ b/test/files/run/repl-parens.scala
@@ -0,0 +1,26 @@
+import scala.tools.partest.ReplTest
+
+object Test extends ReplTest {
+ def code = """
+(2)
+(2 + 2)
+((2 + 2))
+ ((2 + 2))
+ ( (2 + 2))
+ ( (2 + 2 ) )
+5 ; ( (2 + 2 ) ) ; ((5))
+(((2 + 2)), ((2 + 2)))
+(((2 + 2)), ((2 + 2)), 2)
+((((2 + 2)), ((2 + 2)), 2).productIterator ++ Iterator(3) mkString)
+
+55 ; ((2 + 2)) ; (1, 2, 3)
+
+() => 5
+55 ; () => 5
+() => { class X ; new X }
+
+def foo(x: Int)(y: Int)(z: Int) = x+y+z
+foo(5)(10)(15)+foo(5)(10)(15)
+
+ """.trim
+} \ No newline at end of file
diff --git a/test/files/run/repl-paste-2.check b/test/files/run/repl-paste-2.check
index 435592567d..18bd6d2434 100644
--- a/test/files/run/repl-paste-2.check
+++ b/test/files/run/repl-paste-2.check
@@ -4,6 +4,9 @@ Type :help for more information.
scala>
scala> scala> 0123
+
+// Detected repl transcript paste: ctrl-D to finish.
+
res4: Int = 0123
scala> 123
diff --git a/test/files/run/repl-power.check b/test/files/run/repl-power.check
new file mode 100644
index 0000000000..9561c04eca
--- /dev/null
+++ b/test/files/run/repl-power.check
@@ -0,0 +1,17 @@
+Type in expressions to have them evaluated.
+Type :help for more information.
+
+scala> :power
+** Power User mode enabled - BEEP BOOP WHIR **
+** scala.tools.nsc._ has been imported **
+** global._ and definitions._ also imported **
+** New vals! Try repl, intp, global, power **
+** New cmds! :help to discover them **
+** New defs! Type power.<tab> to reveal **
+
+scala> // guarding against "error: reference to global is ambiguous"
+
+scala> global.emptyValDef // "it is imported twice in the same scope by ..."
+res0: global.emptyValDef.type = private val _ = _
+
+scala>
diff --git a/test/files/run/repl-power.scala b/test/files/run/repl-power.scala
new file mode 100644
index 0000000000..9f70ac4b68
--- /dev/null
+++ b/test/files/run/repl-power.scala
@@ -0,0 +1,10 @@
+import scala.tools.partest.ReplTest
+
+object Test extends ReplTest {
+ def code = """
+:power
+// guarding against "error: reference to global is ambiguous"
+global.emptyValDef // "it is imported twice in the same scope by ..."
+ """.trim
+}
+
diff --git a/test/files/run/repl-transcript.check b/test/files/run/repl-transcript.check
index 03162451b6..6d22353882 100644
--- a/test/files/run/repl-transcript.check
+++ b/test/files/run/repl-transcript.check
@@ -4,6 +4,9 @@ Type :help for more information.
scala>
scala> scala> class Bippity
+
+// Detected repl transcript paste: ctrl-D to finish.
+
defined class Bippity
scala> def f = new Bippity