aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty/tools/dotc/repl/InterpreterLoop.scala
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/src/dotty/tools/dotc/repl/InterpreterLoop.scala')
-rw-r--r--compiler/src/dotty/tools/dotc/repl/InterpreterLoop.scala210
1 files changed, 210 insertions, 0 deletions
diff --git a/compiler/src/dotty/tools/dotc/repl/InterpreterLoop.scala b/compiler/src/dotty/tools/dotc/repl/InterpreterLoop.scala
new file mode 100644
index 000000000..b3ac41c55
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/repl/InterpreterLoop.scala
@@ -0,0 +1,210 @@
+package dotty.tools
+package dotc
+package repl
+
+import java.io.{BufferedReader, File, FileReader, PrintWriter}
+import java.io.IOException
+import java.lang.{ClassLoader, System}
+import scala.concurrent.{Future, Await}
+import scala.concurrent.duration.Duration
+import reporting.Reporter
+import core._
+import Contexts._
+import annotation.tailrec
+import scala.concurrent.ExecutionContext.Implicits.global
+
+/** The interactive shell. It provides a read-eval-print loop around
+ * the Interpreter class.
+ * After instantiation, clients should call the `run` method.
+ *
+ * @author Moez A. Abdel-Gawad
+ * @author Lex Spoon
+ * @author Martin Odersky
+ */
+class InterpreterLoop(compiler: Compiler, config: REPL.Config)(implicit ctx: Context) {
+ import config._
+
+ val interpreter = compiler.asInstanceOf[Interpreter]
+
+ private var in = input(interpreter)
+
+ /** The context class loader at the time this object was created */
+ protected val originalClassLoader =
+ Thread.currentThread.getContextClassLoader
+
+ /** A reverse list of commands to replay if the user
+ * requests a :replay */
+ var replayCommandsRev: List[String] = Nil
+
+ /** A list of commands to replay if the user requests a :replay */
+ def replayCommands = replayCommandsRev.reverse
+
+ /** Record a command for replay should the user request a :replay */
+ def addReplay(cmd: String) =
+ replayCommandsRev = cmd :: replayCommandsRev
+
+ /** Close the interpreter */
+ def closeInterpreter()(implicit ctx: Context): Unit = {
+ ctx.reporter.flush()
+ Thread.currentThread.setContextClassLoader(originalClassLoader)
+ }
+
+ /** print a friendly help message */
+ def printHelp(): Unit = {
+ printWelcome()
+ output.println("Type :load followed by a filename to load a Scala file.")
+ output.println("Type :replay to reset execution and replay all previous commands.")
+ output.println("Type :quit to exit the interpreter.")
+ }
+
+ /** Print a welcome message */
+ def printWelcome(): Unit = {
+ output.println(s"Welcome to Scala$version " + " (" +
+ System.getProperty("java.vm.name") + ", Java " + System.getProperty("java.version") + ")." )
+ output.println("Type in expressions to have them evaluated.")
+ output.println("Type :help for more information.")
+ output.flush()
+ }
+
+ val gitHash = ManifestInfo.attributes.getOrElse("Git-Hash", "unknown")
+ val version = s".next (pre-alpha, git-hash: $gitHash)"
+
+ /** The main read-eval-print loop for the interpreter. It calls
+ * `command()` for each line of input.
+ */
+ @tailrec final def repl(line: String = in.readLine(prompt)): Unit =
+ if (line != null) {
+ val (keepGoing, finalLineOpt) = command(line)
+ if (keepGoing) {
+ finalLineOpt.foreach(addReplay)
+ output.flush()
+ repl()
+ }
+ }
+
+ /** interpret all lines from a specified file */
+ def interpretAllFrom(filename: String): Unit = {
+ import java.nio.file.{Files, Paths}
+ import scala.collection.JavaConversions._
+ try {
+ val lines = Files.readAllLines(Paths.get(filename)).mkString("\n")
+ output.println("Loading " + filename + "...")
+ output.flush
+ interpreter.interpret(lines)
+ } catch {
+ case _: IOException =>
+ output.println("Error opening file: " + filename)
+ }
+ }
+
+ /** create a new interpreter and replay all commands so far */
+ def replay(): Unit = {
+ for (cmd <- replayCommands) {
+ output.println("Replaying: " + cmd)
+ output.flush() // because maybe cmd will have its own output
+ command(cmd)
+ output.println
+ }
+ }
+
+ /** Run one command submitted by the user. Three values are returned:
+ * (1) whether to keep running, (2) the line to record for replay,
+ * if any. */
+ def command(line: String): (Boolean, Option[String]) = {
+ def withFile(command: String)(action: String => Unit): Unit = {
+ val spaceIdx = command.indexOf(' ')
+ if (spaceIdx <= 0) {
+ output.println("That command requires a filename to be specified.")
+ return
+ }
+ val filename = command.substring(spaceIdx).trim
+ if (!new File(filename).exists) {
+ output.println("That file does not exist")
+ return
+ }
+ action(filename)
+ }
+
+ val helpRegexp = ":h(e(l(p)?)?)?"
+ val quitRegexp = ":q(u(i(t)?)?)?"
+ val loadRegexp = ":l(o(a(d)?)?)?.*"
+ val replayRegexp = ":r(e(p(l(a(y)?)?)?)?)?.*"
+ val lastOutput = interpreter.lastOutput()
+
+ var shouldReplay: Option[String] = None
+
+ if (line.matches(helpRegexp))
+ printHelp()
+ else if (line.matches(quitRegexp))
+ return (false, None)
+ else if (line.matches(loadRegexp)) {
+ withFile(line)(f => {
+ interpretAllFrom(f)
+ shouldReplay = Some(line)
+ })
+ }
+ else if (line matches replayRegexp)
+ replay()
+ else if (line startsWith ":")
+ output.println("Unknown command. Type :help for help.")
+ else
+ shouldReplay = lastOutput match { // don't interpret twice
+ case Nil => interpretStartingWith(line)
+ case oldRes =>
+ oldRes foreach output.print
+ Some(line)
+ }
+
+ (true, shouldReplay)
+ }
+
+ def silentlyRun(cmds: List[String]): Unit = cmds.foreach { cmd =>
+ interpreter.beQuietDuring(interpreter.interpret(cmd))
+ }
+
+ def silentlyBind(values: Array[(String, Any)]): Unit = values.foreach { case (id, value) =>
+ interpreter.beQuietDuring(
+ interpreter.bind(id, value.asInstanceOf[AnyRef].getClass.getName, value.asInstanceOf[AnyRef]))
+ }
+
+ /** Interpret expressions starting with the first line.
+ * Read lines until a complete compilation unit is available
+ * or until a syntax error has been seen. If a full unit is
+ * read, go ahead and interpret it. Return the full string
+ * to be recorded for replay, if any.
+ */
+ def interpretStartingWith(code: String): Option[String] =
+ interpreter.interpret(code) match {
+ case Interpreter.Success => Some(code)
+ case _ => None
+ }
+/*
+ def loadFiles(settings: Settings) {
+ settings match {
+ case settings: GenericRunnerSettings =>
+ for (filename <- settings.loadfiles.value) {
+ val cmd = ":load " + filename
+ command(cmd)
+ replayCommandsRev = cmd :: replayCommandsRev
+ output.println()
+ }
+ case _ =>
+ }
+ }
+*/
+ def run(): Reporter = {
+ // loadFiles(settings)
+ try {
+ if (!ctx.reporter.hasErrors) { // if there are already errors, no sense to continue
+ printWelcome()
+ silentlyRun(config.initialCommands)
+ silentlyBind(config.boundValues)
+ repl(in.readLine(prompt))
+ silentlyRun(config.cleanupCommands)
+ }
+ } finally {
+ closeInterpreter()
+ }
+ ctx.reporter
+ }
+}