summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLex Spoon <lex@lexspoon.org>2007-01-15 22:35:17 +0000
committerLex Spoon <lex@lexspoon.org>2007-01-15 22:35:17 +0000
commit8bb851f21e29ab1f83776ad830922894a7b9c426 (patch)
treebff22e63fbd75fa332d772b95c1864e27dfa9a60
parent8190c6b5da11d3d05cf492b686fb35265d2fd779 (diff)
downloadscala-8bb851f21e29ab1f83776ad830922894a7b9c426.tar.gz
scala-8bb851f21e29ab1f83776ad830922894a7b9c426.tar.bz2
scala-8bb851f21e29ab1f83776ad830922894a7b9c426.zip
Support multi-line expressions and definitions ...
Support multi-line expressions and definitions in the interpreter.
-rw-r--r--src/compiler/scala/tools/nsc/Interpreter.scala39
-rw-r--r--src/compiler/scala/tools/nsc/InterpreterLoop.scala103
-rw-r--r--src/compiler/scala/tools/nsc/InterpreterResults.scala16
3 files changed, 110 insertions, 48 deletions
diff --git a/src/compiler/scala/tools/nsc/Interpreter.scala b/src/compiler/scala/tools/nsc/Interpreter.scala
index e4dd4b8d6e..e16839956c 100644
--- a/src/compiler/scala/tools/nsc/Interpreter.scala
+++ b/src/compiler/scala/tools/nsc/Interpreter.scala
@@ -18,6 +18,7 @@ import io.PlainFile
import reporters.{ConsoleReporter, Reporter}
import symtab.Flags
import util.SourceFile
+import nsc.{InterpreterResults=>IR}
/** <p>
* An interpreter for Scala code.
@@ -161,8 +162,9 @@ class Interpreter(val settings: Settings, reporter: Reporter, out: PrintWriter)
stringWriter.toString
}
- /** Parse a line into a sequence of trees. */
- private def parse(line: String): List[Tree] = {
+ /** Parse a line into a sequence of trees. Returns None if the input
+ * is incomplete. */
+ private def parse(line: String): Option[List[Tree]] = {
var justNeedsMore = false
reporter.withIncompleteHandler((pos,msg) => {justNeedsMore = true}) {
//reporter.incompleteInputError = (pos,msg) => {justNeedsMore = true}
@@ -177,20 +179,16 @@ class Interpreter(val settings: Settings, reporter: Reporter, out: PrintWriter)
// parse the main code along with the imports
reporter.reset
val trees = simpleParse(codeForImports + line)
- if (justNeedsMore) {
- reporter.error(
- null,
- "Input truncated. Interpreted expressions must fit on one line.")
- // XXX should accept more lines of input
- Nil
- } else if (reporter.hasErrors)
- Nil // the result did not parse, so stop
+ if (justNeedsMore)
+ None
+ else if (reporter.hasErrors)
+ Some(Nil) // the result did not parse, so stop
else {
// parse the imports alone
val importTrees = simpleParse(codeForImports)
// return just the new trees, not the import trees
- trees.drop(importTrees.length)
+ Some(trees.drop(importTrees.length))
}
}
}
@@ -258,22 +256,23 @@ class Interpreter(val settings: Settings, reporter: Reporter, out: PrintWriter)
* e.g. that there were no parse errors.
* </p>
*
- * @param line ...
- * @return ...
*/
- def interpret(line: String): boolean = {
+ def interpret(line: String): IR.Result = {
// parse
- val trees = parse(line)
- if (trees.isEmpty) return false // parse error or empty input
+ val trees = parse(line) match {
+ case None => return IR.Incomplete
+ case Some(Nil) => return IR.Error // parse error or empty input
+ case Some(trees) => trees
+ }
val lineName = newLineName
// figure out what kind of request
val req = buildRequest(trees, line, lineName)
- if (req eq null) return false // a disallowed statement type
+ if (req eq null) return IR.Error // a disallowed statement type
if (!req.compile)
- return false // an error happened during compilation, e.g. a type error
+ return IR.Error // an error happened during compilation, e.g. a type error
val Pair(interpreterResultString, succeeded) = req.loadAndRun
@@ -290,7 +289,7 @@ class Interpreter(val settings: Settings, reporter: Reporter, out: PrintWriter)
if (succeeded)
prevRequests += req
- succeeded
+ if(succeeded) IR.Success else IR.Error
}
/** A counter used for numbering objects created by bind() */
@@ -357,7 +356,7 @@ class Interpreter(val settings: Settings, reporter: Reporter, out: PrintWriter)
/** One line of code submitted by the user for interpretation */
private abstract class Request(val line: String, val lineName: String) {
- val trees = parse(line)
+ val Some(trees) = parse(line)
/** name to use for the object that will compute "line" */
def objectName = lineName + compiler.nme.INTERPRETER_WRAPPER_SUFFIX // make it unlikely to clash with user variables
diff --git a/src/compiler/scala/tools/nsc/InterpreterLoop.scala b/src/compiler/scala/tools/nsc/InterpreterLoop.scala
index b685bbd47b..220cdfc8cf 100644
--- a/src/compiler/scala/tools/nsc/InterpreterLoop.scala
+++ b/src/compiler/scala/tools/nsc/InterpreterLoop.scala
@@ -13,6 +13,7 @@ import java.io.IOException
import scala.tools.nsc.reporters.{Reporter, ConsoleReporter}
import scala.tools.nsc.util.{Position}
+import nsc.{InterpreterResults=>IR}
/** The main loop of the command-line interface to the
* <a href="http://scala.epfl.ch/" target="_top">Scala</a> interpreter.
@@ -21,10 +22,18 @@ import scala.tools.nsc.util.{Position}
* @author Lex Spoon
* @version 1.0
*/
-class InterpreterLoop(in: BufferedReader, out: PrintWriter) {
+class InterpreterLoop(in0: BufferedReader, out: PrintWriter) {
def this() = this(new BufferedReader(new InputStreamReader(System.in)),
new PrintWriter(System.out))
+ /** The input stream from which interpreter commands come */
+ var in = in0
+
+ /** Whether the interpreter is interactive (like normal), or instead
+ * is reading from a file.
+ */
+ var interactive = true
+
var settings: Settings = _ // set by main()
var interpreter: Interpreter = null // set by createInterpreter()
@@ -87,18 +96,23 @@ class InterpreterLoop(in: BufferedReader, out: PrintWriter) {
*/
def repl(): Unit =
while(true) {
- out.print("\nscala> ")
- out.flush
+ if(interactive) {
+ out.print("\nscala> ")
+ out.flush
+ }
var line = in.readLine()
if (line eq null)
return () // assumes null means EOF
- val Pair(keepGoing, shouldReplay) = command(line)
+ val Pair(keepGoing, finalLineMaybe) = command(line)
if (!keepGoing)
return ()
- if (shouldReplay)
- addReplay(line)
+
+ finalLineMaybe match {
+ case Some(finalLine) => addReplay(finalLine)
+ case None => ()
+ }
}
/** interpret all lines from a specified file */
@@ -108,17 +122,20 @@ class InterpreterLoop(in: BufferedReader, out: PrintWriter) {
} catch {
case _:IOException =>
out.println("Error opening file: " + filename)
- null
- }
- if (fileIn eq null) return ()
- val in = new BufferedReader(fileIn)
- while (true) {
- val line = in.readLine
- if (line eq null) {
- fileIn.close
return ()
- }
- command(line)
+ }
+ val oldIn = in
+ val oldInteractive = interactive
+ try {
+ in = new BufferedReader(fileIn)
+ interactive = false
+ out.println("Loading " + filename + "...")
+ out.flush
+ repl()
+ } finally {
+ in = oldIn
+ interactive = oldInteractive
+ fileIn.close
}
}
@@ -133,10 +150,10 @@ class InterpreterLoop(in: BufferedReader, out: PrintWriter) {
}
}
- /** Run one command submitted by the user. Two values are returned:
- * (1) whether to keep running, and (2) whether to record the
- * command for replay. */
- def command(line: String): Pair[Boolean, Boolean] = {
+ /** 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): Pair[Boolean, Option[String]] = {
def withFile(command: String)(action: String => Unit): Unit = {
val spaceIdx = command.indexOf(' ')
if (spaceIdx <= 0) {
@@ -157,35 +174,65 @@ class InterpreterLoop(in: BufferedReader, out: PrintWriter) {
val loadRegexp = ":l(o(a(d)?)?)?.*"
val replayRegexp = ":r(e(p(l(a(y)?)?)?)?)?.*"
- var shouldReplay = false
+ var shouldReplay: Option[String] = None
if (line.matches(helpRegexp))
printHelp
else if (line.matches(quitRegexp))
- return Pair(false, false)
+ return Pair(false, None)
else if (line.matches(compileRegexp)) {
withFile(line)(f => {
interpreter.compileFile(f)
- shouldReplay = true
+ shouldReplay = Some(line)
})
}
else if (line.matches(loadRegexp)) {
withFile(line)(f => {
interpretAllFrom(f)
- shouldReplay = true
+ shouldReplay = Some(line)
})
}
else if (line.matches(replayRegexp))
replay
else if (line.startsWith(":"))
out.println("Unknown command. Type :help for help.")
- else {
- if (interpreter.interpret(line))
- shouldReplay = true
- }
+ else
+ shouldReplay = interpretStartingWith(line)
+
Pair(true, shouldReplay)
}
+ /** 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 IR.Success => Some(code)
+ case IR.Error => None
+ case IR.Incomplete => {
+ if(interactive && code.endsWith("\n\n\n")) {
+ out.println("Two blank lines seen. Aborting this expression.")
+ None
+ } else {
+ if(interactive) {
+ out.print(" | ")
+ out.flush
+ }
+ val nextLine = in.readLine
+ if(nextLine == null)
+ None // end of file
+ else
+ interpretStartingWith(code + "\n" + nextLine)
+ }
+ }
+ }
+ }
+
+
def main(settings: Settings) = {
this.settings = settings
diff --git a/src/compiler/scala/tools/nsc/InterpreterResults.scala b/src/compiler/scala/tools/nsc/InterpreterResults.scala
new file mode 100644
index 0000000000..9ae055abbf
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/InterpreterResults.scala
@@ -0,0 +1,16 @@
+package scala.tools.nsc
+
+object InterpreterResults {
+ /** A result from interpreting one line of input */
+ abstract sealed class Result
+
+ /** The line was interpreted successfully */
+ case object Success extends Result
+
+ /** The line was erroneous in some way */
+ case object Error extends Result
+
+ /** The input was incomplete. The caller should request more
+ * input. */
+ case object Incomplete extends Result
+} \ No newline at end of file