diff options
author | odersky <odersky@gmail.com> | 2016-03-31 11:20:48 +0200 |
---|---|---|
committer | odersky <odersky@gmail.com> | 2016-03-31 11:20:48 +0200 |
commit | 5bd08d437c1365bd8a81cb1b6b9801b443fde96b (patch) | |
tree | e2be21f89de19da9b7ce2ea2391c7cc300f4c515 | |
parent | 8cafcb9455103c34b6ce4344b58ca472a3a1f034 (diff) | |
parent | ef8c1968b2ea407c5b2ddca2fef00eb922e81f8e (diff) | |
download | dotty-5bd08d437c1365bd8a81cb1b6b9801b443fde96b.tar.gz dotty-5bd08d437c1365bd8a81cb1b6b9801b443fde96b.tar.bz2 dotty-5bd08d437c1365bd8a81cb1b6b9801b443fde96b.zip |
Merge pull request #1182 from dotty-staging/repl-fixes
Repl fixes and tests
-rw-r--r-- | src/dotty/tools/dotc/Driver.scala | 2 | ||||
-rw-r--r-- | src/dotty/tools/dotc/Resident.scala | 1 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Contexts.scala | 2 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Definitions.scala | 2 | ||||
-rw-r--r-- | src/dotty/tools/dotc/repl/CompilingInterpreter.scala | 24 | ||||
-rw-r--r-- | src/dotty/tools/dotc/repl/InterpreterLoop.scala | 52 | ||||
-rw-r--r-- | src/dotty/tools/dotc/repl/REPL.scala | 34 | ||||
-rw-r--r-- | test/dotc/tests.scala | 2 | ||||
-rw-r--r-- | test/test/CompilerTest.scala | 17 | ||||
-rw-r--r-- | test/test/TestREPL.scala | 47 | ||||
-rw-r--r-- | tests/repl/import.check | 11 | ||||
-rw-r--r-- | tests/repl/imports.check | 24 | ||||
-rw-r--r-- | tests/repl/multilines.check | 33 | ||||
-rw-r--r-- | tests/repl/onePlusOne.check | 3 |
14 files changed, 191 insertions, 63 deletions
diff --git a/src/dotty/tools/dotc/Driver.scala b/src/dotty/tools/dotc/Driver.scala index 887274fa8..2e78854c1 100644 --- a/src/dotty/tools/dotc/Driver.scala +++ b/src/dotty/tools/dotc/Driver.scala @@ -15,8 +15,6 @@ import scala.util.control.NonFatal */ abstract class Driver extends DotClass { - val prompt = "\ndotc> " - protected def newCompiler(implicit ctx: Context): Compiler protected def emptyReporter: Reporter = new StoreReporter(null) diff --git a/src/dotty/tools/dotc/Resident.scala b/src/dotty/tools/dotc/Resident.scala index 18bb2ff4f..e1b62e4d0 100644 --- a/src/dotty/tools/dotc/Resident.scala +++ b/src/dotty/tools/dotc/Resident.scala @@ -31,6 +31,7 @@ class Resident extends Driver { private val quit = ":q" private val reset = ":reset" + private val prompt = "dotc> " private def getLine() = { Console.print(prompt) diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala index 2fc958a49..fd0cff94e 100644 --- a/src/dotty/tools/dotc/core/Contexts.scala +++ b/src/dotty/tools/dotc/core/Contexts.scala @@ -544,7 +544,7 @@ object Contexts { */ def initialize()(implicit ctx: Context): Unit = { _platform = newPlatform - definitions.init + definitions.init() } def squashed(p: Phase): Phase = { diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index 6f8a8f837..d8c882d5c 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -798,7 +798,7 @@ class Definitions { private[this] var _isInitialized = false private def isInitialized = _isInitialized - def init(implicit ctx: Context) = { + def init()(implicit ctx: Context) = { this.ctx = ctx if (!_isInitialized) { // force initialization of every symbol that is synthesized or hijacked by the compiler diff --git a/src/dotty/tools/dotc/repl/CompilingInterpreter.scala b/src/dotty/tools/dotc/repl/CompilingInterpreter.scala index 7d1da1419..bc898488d 100644 --- a/src/dotty/tools/dotc/repl/CompilingInterpreter.scala +++ b/src/dotty/tools/dotc/repl/CompilingInterpreter.scala @@ -60,6 +60,8 @@ class CompilingInterpreter(out: PrintWriter, ictx: Context) extends Compiler wit import ast.untpd._ import CompilingInterpreter._ + ictx.base.initialize()(ictx) + /** directory to save .class files to */ val virtualDirectory = if (ictx.settings.d.isDefault(ictx)) new VirtualDirectory("(memory)", None) @@ -175,7 +177,7 @@ class CompilingInterpreter(out: PrintWriter, ictx: Context) extends Compiler wit // if (prevRequests.isEmpty) // new Run(this) // initialize the compiler // (not sure this is needed) // parse - parse(indentCode(line)) match { + parse(line) match { case None => Interpreter.Incomplete case Some(Nil) => Interpreter.Error // parse error or empty input case Some(tree :: Nil) if tree.isTerm && !tree.isInstanceOf[Assign] => @@ -271,7 +273,7 @@ class CompilingInterpreter(out: PrintWriter, ictx: Context) extends Compiler wit // header for the wrapper object code.println("object " + objectName + " {") code.print(importsPreamble) - code.println(indentCode(toCompute)) + code.println(toCompute) handlers.foreach(_.extraCodeToEvaluate(this,code)) code.println(importsTrailer) //end the wrapper object @@ -477,7 +479,7 @@ class CompilingInterpreter(out: PrintWriter, ictx: Context) extends Compiler wit addWrapper() if (handler.statement.isInstanceOf[Import]) - preamble.append(handler.statement.toString + ";\n") + preamble.append(handler.statement.show + ";\n") // give wildcard imports a import wrapper all to their own if (handler.importsWildcard) @@ -645,7 +647,7 @@ class CompilingInterpreter(out: PrintWriter, ictx: Context) extends Compiler wit private class ImportHandler(imp: Import) extends StatementHandler(imp) { override def resultExtractionCode(req: Request, code: PrintWriter): Unit = { - code.println("+ \"" + imp.toString + "\\n\"") + code.println("+ \"" + imp.show + "\\n\"") } def isWildcardSelector(tree: Tree) = tree match { @@ -734,20 +736,6 @@ class CompilingInterpreter(out: PrintWriter, ictx: Context) extends Compiler wit /** Clean up a string for output */ private def clean(str: String)(implicit ctx: Context) = truncPrintString(stripWrapperGunk(str)) - - /** Indent some code by the width of the scala> prompt. - * This way, compiler error messages read better. - */ - def indentCode(code: String) = { - val spaces = " " - - stringFrom(str => - for (line <- code.lines) { - str.print(spaces) - str.print(line + "\n") - str.flush() - }) - } } /** Utility methods for the Interpreter. */ diff --git a/src/dotty/tools/dotc/repl/InterpreterLoop.scala b/src/dotty/tools/dotc/repl/InterpreterLoop.scala index eedec3c82..4ac9602e7 100644 --- a/src/dotty/tools/dotc/repl/InterpreterLoop.scala +++ b/src/dotty/tools/dotc/repl/InterpreterLoop.scala @@ -21,10 +21,10 @@ import scala.concurrent.ExecutionContext.Implicits.global * @author Lex Spoon * @author Martin Odersky */ -class InterpreterLoop( - compiler: Compiler, - private var in: InteractiveReader, - out: PrintWriter)(implicit ctx: Context) { +class InterpreterLoop(compiler: Compiler, config: REPL.Config)(implicit ctx: Context) { + import config._ + + private var in = input val interpreter = compiler.asInstanceOf[Interpreter] @@ -52,24 +52,20 @@ class InterpreterLoop( /** print a friendly help message */ def printHelp(): Unit = { printWelcome() - out.println("Type :load followed by a filename to load a Scala file.") - out.println("Type :replay to reset execution and replay all previous commands.") - out.println("Type :quit to exit the interpreter.") + 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 = { - out.println(s"Welcome to Scala$version " + " (" + + output.println(s"Welcome to Scala$version " + " (" + System.getProperty("java.vm.name") + ", Java " + System.getProperty("java.version") + ")." ) - out.println("Type in expressions to have them evaluated.") - out.println("Type :help for more information.") - out.flush() + output.println("Type in expressions to have them evaluated.") + output.println("Type :help for more information.") + output.flush() } - /** Prompt to print when awaiting input */ - val prompt = "scala> " - val continuationPrompt = " | " - val version = ".next (pre-alpha)" /** The first interpreted command always takes a couple of seconds @@ -92,7 +88,7 @@ class InterpreterLoop( val (keepGoing, finalLineOpt) = command(line) if (keepGoing) { finalLineOpt.foreach(addReplay) - out.flush() + output.flush() repl() } } @@ -103,16 +99,16 @@ class InterpreterLoop( new FileReader(filename) } catch { case _: IOException => - out.println("Error opening file: " + filename) + output.println("Error opening file: " + filename) return } val oldIn = in val oldReplay = replayCommandsRev try { val inFile = new BufferedReader(fileIn) - in = new SimpleReader(inFile, out, false) - out.println("Loading " + filename + "...") - out.flush + in = new SimpleReader(inFile, output, false) + output.println("Loading " + filename + "...") + output.flush repl() } finally { in = oldIn @@ -124,10 +120,10 @@ class InterpreterLoop( /** create a new interpreter and replay all commands so far */ def replay(): Unit = { for (cmd <- replayCommands) { - out.println("Replaying: " + cmd) - out.flush() // because maybe cmd will have its own output + output.println("Replaying: " + cmd) + output.flush() // because maybe cmd will have its own output command(cmd) - out.println + output.println } } @@ -138,12 +134,12 @@ class InterpreterLoop( def withFile(command: String)(action: String => Unit): Unit = { val spaceIdx = command.indexOf(' ') if (spaceIdx <= 0) { - out.println("That command requires a filename to be specified.") + output.println("That command requires a filename to be specified.") return } val filename = command.substring(spaceIdx).trim if (!new File(filename).exists) { - out.println("That file does not exist") + output.println("That file does not exist") return } action(filename) @@ -169,7 +165,7 @@ class InterpreterLoop( else if (line matches replayRegexp) replay() else if (line startsWith ":") - out.println("Unknown command. Type :help for help.") + output.println("Unknown command. Type :help for help.") else shouldReplay = interpretStartingWith(line) @@ -188,7 +184,7 @@ class InterpreterLoop( case Interpreter.Error => None case Interpreter.Incomplete => if (in.interactive && code.endsWith("\n\n")) { - out.println("You typed two blank lines. Starting a new command.") + output.println("You typed two blank lines. Starting a new command.") None } else { val nextLine = in.readLine(continuationPrompt) @@ -207,7 +203,7 @@ class InterpreterLoop( val cmd = ":load " + filename command(cmd) replayCommandsRev = cmd :: replayCommandsRev - out.println() + output.println() } case _ => } diff --git a/src/dotty/tools/dotc/repl/REPL.scala b/src/dotty/tools/dotc/repl/REPL.scala index 2d6a3c742..e5ff2d3af 100644 --- a/src/dotty/tools/dotc/repl/REPL.scala +++ b/src/dotty/tools/dotc/repl/REPL.scala @@ -23,27 +23,37 @@ import java.io.{BufferedReader, File, FileReader, PrintWriter} */ class REPL extends Driver { - /** The default input reader */ - def input(implicit ctx: Context): InteractiveReader = { - val emacsShell = System.getProperty("env.emacs", "") != "" - //println("emacsShell="+emacsShell) //debug - if (ctx.settings.Xnojline.value || emacsShell) new SimpleReader() - else InteractiveReader.createDefault() - } - - /** The defult output writer */ - def output: PrintWriter = new NewLinePrintWriter(new ConsoleWriter, true) + lazy val config = new REPL.Config override def newCompiler(implicit ctx: Context): Compiler = - new repl.CompilingInterpreter(output, ctx) + new repl.CompilingInterpreter(config.output, ctx) override def sourcesRequired = false override def doCompile(compiler: Compiler, fileNames: List[String])(implicit ctx: Context): Reporter = { if (fileNames.isEmpty) - new InterpreterLoop(compiler, input, output).run() + new InterpreterLoop(compiler, config).run() else ctx.error(s"don't now what to do with $fileNames%, %") ctx.reporter } } + +object REPL { + class Config { + val prompt = "scala> " + val continuationPrompt = " | " + val version = ".next (pre-alpha)" + + /** The default input reader */ + def input(implicit ctx: Context): InteractiveReader = { + val emacsShell = System.getProperty("env.emacs", "") != "" + //println("emacsShell="+emacsShell) //debug + if (ctx.settings.Xnojline.value || emacsShell) new SimpleReader() + else InteractiveReader.createDefault() + } + + /** The default output writer */ + def output: PrintWriter = new NewLinePrintWriter(new ConsoleWriter, true) + } +} diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index d6bafe6be..48242f2d1 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -46,6 +46,7 @@ class tests extends CompilerTest { val negDir = testsDir + "neg/" val runDir = testsDir + "run/" val newDir = testsDir + "new/" + val replDir = testsDir + "repl/" val sourceDir = "./src/" val dottyDir = sourceDir + "dotty/" @@ -112,6 +113,7 @@ class tests extends CompilerTest { @Test def pos_859 = compileFile(posSpecialDir, "i859", scala2mode)(allowDeepSubtypes) @Test def new_all = compileFiles(newDir, twice) + @Test def repl_all = replFiles(replDir) @Test def neg_all = compileFiles(negDir, verbose = true, compileSubDirs = false) @Test def neg_typedIdents() = compileDir(negDir, "typedIdents") diff --git a/test/test/CompilerTest.scala b/test/test/CompilerTest.scala index ef2f719fc..1ca836133 100644 --- a/test/test/CompilerTest.scala +++ b/test/test/CompilerTest.scala @@ -5,12 +5,12 @@ import dotty.tools.dotc.{Main, Bench, Driver} import dotty.tools.dotc.reporting.Reporter import dotty.tools.dotc.util.SourcePosition import dotty.tools.dotc.config.CompilerCommand +import dotty.tools.io.PlainFile import scala.collection.mutable.ListBuffer import scala.reflect.io.{ Path, Directory, File => SFile, AbstractFile } import scala.tools.partest.nest.{ FileManager, NestUI } import scala.annotation.tailrec import java.io.{ RandomAccessFile, File => JFile } -import dotty.tools.io.PlainFile import org.junit.Test @@ -205,7 +205,22 @@ abstract class CompilerTest { } } + def replFile(prefix: String, fileName: String): Unit = { + val path = s"$prefix$fileName" + val f = new PlainFile(path) + val repl = new TestREPL(new String(f.toCharArray)) + repl.process(Array[String]()) + repl.check() + } + def replFiles(path: String): Unit = { + val dir = Directory(path) + val fileNames = dir.files.toArray.map(_.jfile.getName).filter(_ endsWith ".check") + for (name <- fileNames) { + log(s"testing $path$name") + replFile(path, name) + } + } // ========== HELPERS ============= diff --git a/test/test/TestREPL.scala b/test/test/TestREPL.scala new file mode 100644 index 000000000..d01038c43 --- /dev/null +++ b/test/test/TestREPL.scala @@ -0,0 +1,47 @@ +package test + +import dotty.tools.dotc.repl._ +import dotty.tools.dotc.core.Contexts.Context +import collection.mutable +import java.io.StringWriter + +/** A subclass of REPL used for testing. + * It takes a transcript of a REPL session in `script`. The transcript + * starts with the first input prompt `scala> ` and ends with `scala> :quit` and a newline. + * Invoking `process()` on the `TestREPL` runs all input lines and + * collects then interleaved with REPL output in a string writer `out`. + * Invoking `check()` checks that the collected output matches the original + * `script`. + */ +class TestREPL(script: String) extends REPL { + + private val out = new StringWriter() + + override lazy val config = new REPL.Config { + override val output = new NewLinePrintWriter(out) + + override def input(implicit ctx: Context) = new InteractiveReader { + val lines = script.lines + def readLine(prompt: String): String = { + val line = lines.next + if (line.startsWith(prompt) || line.startsWith(continuationPrompt)) { + output.println(line) + line.drop(prompt.length) + } + else readLine(prompt) + } + val interactive = false + } + } + + def check() = { + out.close() + val printed = out.toString + val transcript = printed.drop(printed.indexOf(config.prompt)) + if (transcript.toString != script) { + println("input differs from transcript:") + println(transcript) + assert(false) + } + } +}
\ No newline at end of file diff --git a/tests/repl/import.check b/tests/repl/import.check new file mode 100644 index 000000000..ccaa52190 --- /dev/null +++ b/tests/repl/import.check @@ -0,0 +1,11 @@ +scala> import collection.mutable._ +import collection.mutable._ +scala> val buf = new ListBuffer[Int] +buf: scala.collection.mutable.ListBuffer[Int] = ListBuffer() +scala> buf += 22 +res0: scala.collection.mutable.ListBuffer[Int] = ListBuffer(22) +scala> buf ++= List(1, 2, 3) +res1: scala.collection.mutable.ListBuffer[Int] = ListBuffer(22, 1, 2, 3) +scala> buf.toList +res2: scala.collection.immutable.List[Int] = List(22, 1, 2, 3) +scala> :quit diff --git a/tests/repl/imports.check b/tests/repl/imports.check new file mode 100644 index 000000000..3fa103283 --- /dev/null +++ b/tests/repl/imports.check @@ -0,0 +1,24 @@ +scala> import scala.collection.mutable +import scala.collection.mutable +scala> val buf = mutable.ListBuffer[Int]() +buf: scala.collection.mutable.ListBuffer[Int] = ListBuffer() +scala> object o { + | val xs = List(1, 2, 3) + | } +defined module o +scala> import o._ +import o._ +scala> buf += xs +<console>:11: error: type mismatch: + found : scala.collection.immutable.List[Int](o.xs) + required: String +buf += xs + ^ +<console>:11: error: type mismatch: + found : String + required: scala.collection.mutable.ListBuffer[Int] +buf += xs +^ +scala> buf ++= xs +res1: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3) +scala> :quit diff --git a/tests/repl/multilines.check b/tests/repl/multilines.check new file mode 100644 index 000000000..3bc32707e --- /dev/null +++ b/tests/repl/multilines.check @@ -0,0 +1,33 @@ +scala> val x = """alpha + | + | omega""" +x: String = +alpha + +omega +scala> val y = """abc + | |def + | |ghi + | """.stripMargin +y: String = +abc +def +ghi + +scala> val z = { + | def square(x: Int) = x * x + | val xs = List(1, 2, 3) + | square(xs) + | } +<console>:8: error: type mismatch: + found : scala.collection.immutable.List[Int](xs) + required: Int + square(xs) + ^ +scala> val z = { + | def square(x: Int) = x * x + | val xs = List(1, 2, 3) + | xs.map(square) + | } +z: scala.collection.immutable.List[Int] = List(1, 4, 9) +scala> :quit diff --git a/tests/repl/onePlusOne.check b/tests/repl/onePlusOne.check new file mode 100644 index 000000000..9db6e6817 --- /dev/null +++ b/tests/repl/onePlusOne.check @@ -0,0 +1,3 @@ +scala> 1+1 +res0: Int = 2 +scala> :quit |