summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLex Spoon <lex@lexspoon.org>2006-03-30 15:44:56 +0000
committerLex Spoon <lex@lexspoon.org>2006-03-30 15:44:56 +0000
commit542401df8d377bc7b6f4c0b88edc37baa95dacf0 (patch)
treefe91fb9f93e6f656135b1789c15442456e5c63a8 /src
parent05cde954425df3efdf3189eab89e2c0aef515f3f (diff)
downloadscala-542401df8d377bc7b6f4c0b88edc37baa95dacf0.tar.gz
scala-542401df8d377bc7b6f4c0b88edc37baa95dacf0.tar.bz2
scala-542401df8d377bc7b6f4c0b88edc37baa95dacf0.zip
added :replay to the interpreter
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/tools/nsc/ConsoleWriter.scala14
-rw-r--r--src/compiler/scala/tools/nsc/Interpreter.scala56
-rw-r--r--src/compiler/scala/tools/nsc/InterpreterLoop.scala137
3 files changed, 141 insertions, 66 deletions
diff --git a/src/compiler/scala/tools/nsc/ConsoleWriter.scala b/src/compiler/scala/tools/nsc/ConsoleWriter.scala
new file mode 100644
index 0000000000..402d068f96
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/ConsoleWriter.scala
@@ -0,0 +1,14 @@
+package scala.tools.nsc
+import java.io.Writer
+
+/** A Writer that writes onto the Scala Console */
+class ConsoleWriter extends Writer {
+ def close = flush
+
+ def flush = Console.flush
+
+ def write(cbuf: Array[char], off: int, len: int): Unit =
+ write(new String(cbuf.subArray(off, off+len-1)))
+
+ override def write(str: String): Unit = Console.print(str)
+}
diff --git a/src/compiler/scala/tools/nsc/Interpreter.scala b/src/compiler/scala/tools/nsc/Interpreter.scala
index 6a8258b02c..fd77e89c0d 100644
--- a/src/compiler/scala/tools/nsc/Interpreter.scala
+++ b/src/compiler/scala/tools/nsc/Interpreter.scala
@@ -6,10 +6,10 @@
package scala.tools.nsc
-import reporters.Reporter
+import reporters._
import nsc.util.SourceFile
import scala.tools.util.PlainFile
-import java.io.{File, Writer, PrintWriter, StringWriter}
+import java.io._
import nsc.ast.parser.SyntaxAnalyzer
import scala.collection.mutable.{ListBuffer, HashSet, ArrayBuffer}
import scala.collection.immutable.{Map, ListMap}
@@ -42,7 +42,7 @@ import symtab.Flags
The main weakness is that redefining classes and methods is not handled
properly, because rebinding at the Java level is technically difficult.
*/
-class Interpreter(val compiler: Global, output: (String => Unit)) {
+class Interpreter(val settings: Settings, reporter: Reporter, out: PrintWriter) {
import symtab.Names
import compiler.Traverser
import compiler.{Tree, TermTree,
@@ -53,12 +53,15 @@ class Interpreter(val compiler: Global, output: (String => Unit)) {
import compiler.Symbol
import compiler.Name
- /** construct an interpreter that prints to the compiler's reporter */
- def this(compiler: Global) = {
- this(compiler, str: String => compiler.reporter.info(null, str, true))
- }
+ /** construct an interpreter that reports to Console */
+ def this(settings: Settings) =
+ this(settings,
+ new ConsoleReporter,
+ new PrintWriter(new ConsoleWriter, true))
- private def reporter = compiler.reporter
+ /** construct an interpreter that uses the specified in and out streams */
+ def this(settings: Settings, out: PrintWriter) =
+ this(settings, new ConsoleReporter(null, out), out)
/** whether to print out result lines */
private var printResults: Boolean = true
@@ -73,7 +76,10 @@ class Interpreter(val compiler: Global, output: (String => Unit)) {
/* set up the compiler's output directory */
- compiler.settings.outdir.value = classfilePath.getPath
+ settings.outdir.value = classfilePath.getPath
+
+ /** the compiler to compile expressions with */
+ val compiler = new Global(settings, reporter)
/** class loader used to load compiled code */
/* A single class loader is used for all commands interpreted by this Interpreter.
@@ -207,35 +213,41 @@ class Interpreter(val compiler: Global, output: (String => Unit)) {
/** 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. */
- def interpret(line: String): Unit = {
+ strings.
+
+ The return value is whether the line was interpreter successfully,
+ e.g. that there were no parse errors.*/
+ def interpret(line: String): boolean = {
// parse
val trees = parse(line)
- if(trees.isEmpty) return () // parse error or empty input
+ if(trees.isEmpty) return false // parse error or empty input
val lineName = newLineName
// figure out what kind of request
val req = buildRequest(trees, line, lineName)
- if(req == null) return () // a disallowed statement type
+ if(req == null) return false // a disallowed statement type
if(!req.compile)
- return () // an error happened during compilation, e.g. a type error
+ return false // an error happened during compilation, e.g. a type error
- val interpreterResultString = req.loadAndRun
+ val Pair(interpreterResultString, succeeded) = req.loadAndRun
if(printResults) {
// print the result
- output(interpreterResultString)
+ out.print(interpreterResultString)
// print out types of functions; they are not printed in the
// request printout
- output(req.defTypesSummary)
+ out.print(req.defTypesSummary)
}
// book-keeping
- prevRequests += req
+ if(succeeded)
+ prevRequests += req
+
+ succeeded
}
@@ -445,18 +457,20 @@ class Interpreter(val compiler: Global, output: (String => Unit)) {
}
/** load and run the code using reflection */
- def loadAndRun: String = {
+ def loadAndRun: Pair[String,boolean] = {
val interpreterResultObject: Class = Class.forName(resultObjectName,true,classLoader)
val resultValMethod: java.lang.reflect.Method =
interpreterResultObject.getMethod("result",null)
try {
- resultValMethod.invoke(interpreterResultObject,null).toString()
+ Pair(resultValMethod.invoke(interpreterResultObject,null).toString(),
+ true)
} catch {
case e => {
def caus(e: Throwable): Throwable =
if(e.getCause == null) e else caus(e.getCause)
val orig = caus(e)
- stringFrom(str => orig.printStackTrace(str))
+ Pair(stringFrom(str => orig.printStackTrace(str)),
+ false)
}
}
}
diff --git a/src/compiler/scala/tools/nsc/InterpreterLoop.scala b/src/compiler/scala/tools/nsc/InterpreterLoop.scala
index 84f6c12a7a..52ecb7a37a 100644
--- a/src/compiler/scala/tools/nsc/InterpreterLoop.scala
+++ b/src/compiler/scala/tools/nsc/InterpreterLoop.scala
@@ -6,7 +6,7 @@
package scala.tools.nsc
-import java.io.{BufferedReader, File, FileReader, IOException, InputStreamReader, PrintWriter}
+import java.io._
import scala.tools.nsc.reporters.{Reporter, ConsoleReporter}
import scala.tools.nsc.util.{Position}
@@ -17,9 +17,47 @@ class InterpreterLoop(in: BufferedReader, out: PrintWriter) {
def this() = this(new BufferedReader(new InputStreamReader(System.in)),
new PrintWriter(System.out))
- val reporter = new ConsoleReporter(in, out)
+ var settings: Settings = _ // set by main()
+ var interpreter: Interpreter = null // set by createInterpreter()
+
+ /** 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 requset a :replay */
+ def addReplay(cmd: String) =
+ replayCommandsRev = cmd :: replayCommandsRev
+
+
+ /** Close the interpreter, if there is one, and set
+ * interpreter to null. */
+ def closeInterpreter = {
+ if(interpreter != null) {
+ interpreter.close
+ interpreter = null
+ }
+ }
+
+ /* As soon as the Eclipse plugin no longer needs it, delete uglinessxxx,
+ * parentClassLoader0, and the parentClassLoader method in Interpreter
+ */
+ var uglinessxxx: ClassLoader = _
+ def parentClassLoader0: ClassLoader = uglinessxxx
+
+
+ /** Create a new interpreter. Close the old one, if there
+ * is one. */
+ def createInterpreter = {
+ closeInterpreter
+
+ interpreter = new Interpreter(settings, out) {
+ override protected def parentClassLoader = parentClassLoader0;
+ }
+ }
- var interpreter: Interpreter = _
/** print a friendly help message */
def printHelp = {
@@ -28,6 +66,7 @@ class InterpreterLoop(in: BufferedReader, out: PrintWriter) {
out.println("Type :quit to exit the interpreter.")
out.println("Type :compile followed by a filename to compile a complete Scala file.")
out.println("Type :load followed by a filename to load a sequence of interpreter commands.")
+ out.println("Type :replay to reset execution and replay all previous commands.")
out.println("Type :help to repeat this message later.")
}
@@ -48,17 +87,6 @@ class InterpreterLoop(in: BufferedReader, out: PrintWriter) {
}
}
- /** interpret one line of code submitted by the user */
- def interpretOne(line: String): Unit = {
- try {
- interpreter.interpret(line)
- } catch {
- case e: Exception =>
- out.println("Exception occurred: " + e.getMessage())
- //e.printStackTrace()
- }
- }
-
/** interpret all lines from a specified file */
def interpretAllFrom(filename: String): Unit = {
val fileIn = try {
@@ -80,6 +108,17 @@ class InterpreterLoop(in: BufferedReader, out: PrintWriter) {
}
}
+ /** create a new interpreter and replay all commands so far */
+ def replay = {
+ closeInterpreter
+ createInterpreter
+ for(val cmd <- replayCommands) {
+ out.println("Replaying: " + cmd)
+ command(cmd)
+ out.println
+ }
+ }
+
/** run one command submitted by the user */
def command(line: String): Boolean = {
def withFile(command: String)(action: String => Unit): Unit = {
@@ -89,44 +128,63 @@ class InterpreterLoop(in: BufferedReader, out: PrintWriter) {
return ()
}
val filename = command.substring(spaceIdx).trim
+ if(! new File(filename).exists) {
+ out.println("That file does not exist")
+ return ()
+ }
+
action(filename)
}
- val helpRegexp = ":h(e(l(p)?)?)?";
- val quitRegexp = ":q(u(i(t)?)?)?";
- val compileRegexp = ":c(o(m(p(i(l(e)?)?)?)?)?)?.*";
- val loadRegexp = ":l(o(a(d)?)?)?.*";
+ val helpRegexp = ":h(e(l(p)?)?)?"
+ val quitRegexp = ":q(u(i(t)?)?)?"
+ val compileRegexp = ":c(o(m(p(i(l(e)?)?)?)?)?)?.*"
+ val loadRegexp = ":l(o(a(d)?)?)?.*"
+ val replayRegexp = ":r(e(p(l(a(y)?)?)?)?)?.*"
+
if (line.matches(helpRegexp))
printHelp
else if (line.matches(quitRegexp))
return false
- else if (line.matches(compileRegexp))
- withFile(line)(f => interpreter.compile(f))
- else if (line.matches(loadRegexp))
- withFile(line)(f => interpretAllFrom(f))
+ else if (line.matches(compileRegexp)) {
+ withFile(line)(f => {
+ interpreter.compile(f)
+ addReplay(line)
+ })
+ }
+ else if (line.matches(loadRegexp)) {
+ withFile(line)(f => {
+ interpretAllFrom(f)
+ addReplay(line)
+ })
+ }
+ else if (line.matches(replayRegexp))
+ replay
else if (line.startsWith(":"))
out.println("Unknown command. Type :help for help.")
else if (line.startsWith("#!/")) // skip the first line of Unix scripts
()
- else if (line.startsWith("exec scalaint ")) // skip the second line of Unix scripts
- ()
- else
- interpretOne(line)
+ else {
+ if(interpreter.interpret(line))
+ addReplay(line)
+ }
true
}
- /* As soon as the Eclipse plugin no longer needs it, delete uglinessxxx,
- * parentClassLoader0, and the parentClassLoader method in Interpreter
- */
- var uglinessxxx: ClassLoader = _
- def parentClassLoader0: ClassLoader = uglinessxxx
/** process command-line arguments and do as they request */
def main(args: Array[String]): unit = {
def error1(msg: String): Unit = out.println("scalaint: " + msg)
val command = new InterpreterCommand(List.fromArray(args), error1)
+ settings = command.settings
+
+ uglinessxxx =
+ new java.net.URLClassLoader(
+ settings.classpath.value.split(File.pathSeparator).
+ map(s => new File(s).toURL),
+ ClassLoader.getSystemClassLoader)
+
- reporter.prompt = command.settings.prompt.value
if (!command.ok || command.settings.help.value) {
// either the command line is wrong, or the user
@@ -136,18 +194,7 @@ class InterpreterLoop(in: BufferedReader, out: PrintWriter) {
return ()
}
-
- val compiler = new Global(command.settings, reporter)
-
- uglinessxxx =
- new java.net.URLClassLoader(
- compiler.settings.classpath.value.split(File.pathSeparator).
- map(s => new File(s).toURL),
- ClassLoader.getSystemClassLoader)
-
- interpreter = new Interpreter(compiler, out.print) {
- override protected def parentClassLoader = parentClassLoader0;
- }
+ createInterpreter
try {
if (!command.files.isEmpty) {
@@ -162,7 +209,7 @@ class InterpreterLoop(in: BufferedReader, out: PrintWriter) {
repl
}
} finally {
- interpreter.close
+ closeInterpreter
}
}