summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2011-05-31 05:08:25 +0000
committerPaul Phillips <paulp@improving.org>2011-05-31 05:08:25 +0000
commitedad717cc1934d80dc0b6a9af528eed8ef4b30b6 (patch)
treeabd8506f32326d513177569192c0ae5d546cae8a /src
parent35f7c2bde563b501e9661aace1e1610990b51544 (diff)
downloadscala-edad717cc1934d80dc0b6a9af528eed8ef4b30b6.tar.gz
scala-edad717cc1934d80dc0b6a9af528eed8ef4b30b6.tar.bz2
scala-edad717cc1934d80dc0b6a9af528eed8ef4b30b6.zip
Working on the finer points of the Total Repl E...
Working on the finer points of the Total Repl Experience. Some of what is in this patch: -- Instantaneous repl startup. Closes #4561. -- SIGINT handler installed last. If you've been annoyed at the repl being difficult to interrupt during its initialization, this one is especially for you. -- Started abstracting out some bits which would go into an API and reconfiguring the repl in those terms. For a good time, call S-C-A-L-A -Dscala.repl.power. No review.
Diffstat (limited to 'src')
-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.scala110
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/ILoopInit.scala124
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/IMain.scala170
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/JLineReader.scala42
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/ReplReporter.scala22
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/package.scala2
8 files changed, 431 insertions, 235 deletions
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 dc2a653c83..51f8da6063 100644
--- a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala
@@ -8,12 +8,11 @@ package interpreter
import Predef.{ println => _, _ }
import java.io.{ BufferedReader, FileReader, PrintWriter }
+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 }
@@ -35,6 +34,7 @@ import io.{ File, Sources }
class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
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))
@@ -79,34 +79,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 +115,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 +154,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 +174,24 @@ 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) = {
+ if (in.currentLine == "") {
+ in.eraseLine()
+ echo(msg)
+ echoNoNL(prompt)
+ }
+ else {
+ 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()
}
@@ -568,16 +538,19 @@ 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 = {
+ 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
}
}
@@ -655,7 +628,7 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
def enablePowerMode() = {
replProps.power setValue true
power.unleash()
- echo(power.banner)
+ echoAndRefresh(power.banner)
}
def verbosity() = {
@@ -806,7 +779,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)
)
@@ -816,7 +789,6 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
SimpleReader()
}
}
-
def process(settings: Settings): Boolean = {
this.settings = settings
createInterpreter()
@@ -824,7 +796,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)
@@ -832,21 +809,18 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
if (intp.reporter.hasErrors)
return false
+ // 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.
+ intp initialize initializedCallback()
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()
- }
+
+ try loop()
+ catch AbstractOrMissingHandler()
finally closeInterpreter()
+
true
}
@@ -952,7 +926,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..dbd4d77a44
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/interpreter/ILoopInit.scala
@@ -0,0 +1,124 @@
+/* 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)
+ if (isReplDebug || isReplPower)
+ echo("[info] started at " + new java.util.Date)
+ }
+
+ private def asyncMessage(msg: String) {
+ if (isReplDebug || 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 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 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 elapsed() = "%.3f".format((System.nanoTime - initStart).toDouble / 1000000000L)
+
+ // Receives a single message once the interpreter is initialized.
+ private val initializedReactor = io.spawn {
+ withLock(initCompilerCondition.await())
+ asyncMessage("[info] compiler init time: " + elapsed() + " s.")
+ postInitialization()
+ }
+
+ // 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())
+
+ // called from main repl loop
+ protected def awaitInitialized() {
+ if (!initIsComplete)
+ withLock { while (!initIsComplete) initLoopCondition.await() }
+ }
+ // called once after init condition is signalled
+ protected def postInitialization() {
+ addThunk(intp.setContextClassLoader())
+ // do this last to avoid annoying uninterruptible startups
+ addThunk(installSigIntHandler())
+ if (isReplPower)
+ addThunk(enablePowerMode())
+
+ runThunks()
+ initIsComplete = true
+ 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 2594df90d1..803ef0225a 100644
--- a/src/compiler/scala/tools/nsc/interpreter/IMain.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/IMain.scala
@@ -12,10 +12,9 @@ import java.lang.reflect
import java.net.URL
import util._
import io.VirtualDirectory
-import reporters.{ ConsoleReporter, Reporter }
+import reporters._
import symtab.Flags
import scala.reflect.internal.Names
-import scala.tools.nsc.interpreter.{ Results => IR }
import scala.tools.util.PathResolver
import scala.tools.nsc.util.{ ScalaClassLoader, Exceptional }
import ScalaClassLoader.URLClassLoader
@@ -91,7 +90,7 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
}
/** reporter */
- lazy val reporter: ConsoleReporter = new IMain.ReplReporter(this)
+ lazy val reporter: ReplReporter = new ReplReporter(this)
import reporter.{ printMessage, withoutTruncating }
// not sure if we have some motivation to print directly to console
@@ -110,55 +109,35 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
private var _initializeComplete = false
def isInitializeComplete = _initializeComplete
- private def _initialize(): Boolean = {
- val source = """
- |class $repl_$init {
- | scala.collection.immutable.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
+ }
}
/** the public, go through the future compiler */
lazy val global: Global = {
- initialize()
-
+ // 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
@@ -382,26 +361,6 @@ 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)
@@ -683,12 +642,6 @@ 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)
@@ -974,76 +927,11 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
}
}
- // 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] = {
- repldbg("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 "$"))
- repldbg("decls: " + decls)
- decls.lastOption map (decl => typeCleanser(sym, decl.name))
- }
- case _ =>
- None
- }
- }
-
- // 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
+ 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] = {
- repldbg("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
- }
+ 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 }
@@ -1165,16 +1053,4 @@ object IMain {
}
}
}
-
- 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)
- }
- }
}
diff --git a/src/compiler/scala/tools/nsc/interpreter/JLineReader.scala b/src/compiler/scala/tools/nsc/interpreter/JLineReader.scala
index d222a80196..ed61240542 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,31 @@ 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 redrawLine() = consoleReader.redrawLineAndFlush()
def eraseLine() = {
- while (consoleReader.delete()) { }
- // consoleReader.eraseLine()
+ // while (consoleReader.delete()) { }
+ consoleReader.eraseLine()
}
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/ReplReporter.scala b/src/compiler/scala/tools/nsc/interpreter/ReplReporter.scala
new file mode 100644
index 0000000000..ab0911d374
--- /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(msg)
+ }
+}
diff --git a/src/compiler/scala/tools/nsc/interpreter/package.scala b/src/compiler/scala/tools/nsc/interpreter/package.scala
index 1865e90e92..4309ceaa4a 100644
--- a/src/compiler/scala/tools/nsc/interpreter/package.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/package.scala
@@ -30,6 +30,8 @@ package object interpreter extends ReplConfig with ReplStrings {
type InputStream = java.io.InputStream
type OutputStream = java.io.OutputStream
+ val IR = Results
+
private[nsc] implicit def enrichClass[T](clazz: Class[T]) = new RichClass[T](clazz)
private[interpreter] implicit def javaCharSeqCollectionToScala(xs: JCollection[_ <: CharSequence]): List[String] = {
import collection.JavaConverters._