Skip to content

Repl fixes and tests #1182

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Mar 31, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions src/dotty/tools/dotc/Driver.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions src/dotty/tools/dotc/Resident.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/core/Contexts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,7 @@ object Contexts {
*/
def initialize()(implicit ctx: Context): Unit = {
_platform = newPlatform
definitions.init
definitions.init()
}

def squashed(p: Phase): Phase = {
Expand Down
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
24 changes: 6 additions & 18 deletions src/dotty/tools/dotc/repl/CompilingInterpreter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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] =>
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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. */
Expand Down
52 changes: 24 additions & 28 deletions src/dotty/tools/dotc/repl/InterpreterLoop.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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]

Expand Down Expand Up @@ -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
Expand All @@ -92,7 +88,7 @@ class InterpreterLoop(
val (keepGoing, finalLineOpt) = command(line)
if (keepGoing) {
finalLineOpt.foreach(addReplay)
out.flush()
output.flush()
repl()
}
}
Expand All @@ -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
Expand All @@ -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
}
}

Expand All @@ -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)
Expand All @@ -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)

Expand All @@ -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)
Expand All @@ -207,7 +203,7 @@ class InterpreterLoop(
val cmd = ":load " + filename
command(cmd)
replayCommandsRev = cmd :: replayCommandsRev
out.println()
output.println()
}
case _ =>
}
Expand Down
34 changes: 22 additions & 12 deletions src/dotty/tools/dotc/repl/REPL.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
2 changes: 2 additions & 0 deletions test/dotc/tests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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/"
Expand Down Expand Up @@ -109,6 +110,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")
Expand Down
17 changes: 16 additions & 1 deletion test/test/CompilerTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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 =============

Expand Down
47 changes: 47 additions & 0 deletions test/test/TestREPL.scala
Original file line number Diff line number Diff line change
@@ -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)
}
}
}
11 changes: 11 additions & 0 deletions tests/repl/import.check
Original file line number Diff line number Diff line change
@@ -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
Loading