summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/IMain.scala73
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala20
-rw-r--r--test/files/run/repl-bare-expr.check36
-rw-r--r--test/files/run/repl-bare-expr.scala16
4 files changed, 111 insertions, 34 deletions
diff --git a/src/compiler/scala/tools/nsc/interpreter/IMain.scala b/src/compiler/scala/tools/nsc/interpreter/IMain.scala
index 61c2eed278..e14f6debab 100644
--- a/src/compiler/scala/tools/nsc/interpreter/IMain.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/IMain.scala
@@ -426,45 +426,70 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
*/
private def buildRequest(line: String, trees: List[Tree]): Request = new Request(line, trees)
+ // rewriting "5 // foo" to "val x = { 5 // foo }" creates broken code because
+ // the close brace is commented out. Strip single-line comments.
+ private def removeComments(line: String): String = {
+ line.lines map (s => s indexOf "//" match {
+ case -1 => s
+ case idx => s take idx
+ }) mkString "\n"
+ }
+ // Given an expression like 10 * 10 * 10 we receive the parent tree positioned
+ // at a '*'. So look at each subtree and find the earliest of all positions.
+ private def earliestPosition(tree: Tree): Int = {
+ var pos = Int.MaxValue
+ tree foreach (t => pos = math.min(pos, t.pos.startOrPoint))
+ pos
+ }
+
private def requestFromLine(line: String, synthetic: Boolean): Either[IR.Result, Request] = {
- val trees = parse(indentCode(line)) match {
+ val content = indentCode(line)
+ val trees = parse(content) match {
case None => return Left(IR.Incomplete)
case Some(Nil) => return Left(IR.Error) // parse error or empty input
case Some(trees) => trees
}
+ // If the last tree is a bare expression, pinpoint where it begins using the
+ // AST node position and snap the line off there. Rewrite the code embodied
+ // by the last tree as a ValDef instead, so we can access the value.
+ trees.last match {
+ case _:Assign => // we don't want to include assignments
+ case _:TermTree | _:Ident | _:Select => // ... but do want other unnamed terms.
+ // The position of the last tree, and the source code split there.
+ val lastpos = earliestPosition(trees.last)
+ val (l1, l2) = content splitAt lastpos
+ val l2body = removeComments(l2).trim
+ val varName = if (synthetic) freshInternalVarName() else freshUserVarName()
+ val valDef = "val " + varName + " = { " + l2body + " }"
+
+ DBG(List(
+ " line" -> line,
+ "content" -> content,
+ " was" -> l2,
+ " l2body" -> l2body,
+ " now" -> valDef) map {
+ case (label, s) => label + ": '" + s + "'"
+ } mkString "\n"
+ )
- // use synthetic vars to avoid filling up the resXX slots
- def varName = if (synthetic) freshInternalVarName() else freshUserVarName()
-
- // Treat a single bare expression specially. This is necessary due to it being hard to
- // modify code at a textual level, and it being hard to submit an AST to the compiler.
- if (trees.size == 1) trees.head match {
- case _:Assign => // we don't want to include assignments
- case _:TermTree | _:Ident | _:Select => // ... but do want these as valdefs.
- requestFromLine("val %s =\n%s".format(varName, line), synthetic) match {
+ // Rewriting "foo ; bar ; 123"
+ // to "foo ; bar ; val resXX = 123"
+ requestFromLine(l1 + " ;\n" + valDef + " ;\n", synthetic) match {
case Right(req) => return Right(req withOriginalLine line)
case x => return x
}
- case _ =>
+ case _ =>
}
-
- // figure out what kind of request
Right(buildRequest(line, trees))
}
/**
- * 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.
- *
- *
- * The return value is whether the line was interpreter successfully,
- * e.g. that there were no parse errors.
- *
+ * 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.
*
- * @param line ...
- * @return ...
+ * The return value is whether the line was interpreter successfully,
+ * e.g. that there were no parse errors.
*/
def interpret(line: String): IR.Result = interpret(line, false)
def interpret(line: String, synthetic: Boolean): IR.Result = {
diff --git a/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala b/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala
index 99112d9151..33f21165b4 100644
--- a/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala
@@ -52,15 +52,15 @@ trait MemberHandlers {
}
def chooseHandler(member: Tree): MemberHandler = member match {
- case member: DefDef => new DefHandler(member)
- case member: ValDef => new ValHandler(member)
- case member@Assign(Ident(_), _) => new AssignHandler(member)
- case member: ModuleDef => new ModuleHandler(member)
- case member: ClassDef => new ClassHandler(member)
- case member: TypeDef => new TypeAliasHandler(member)
- case member: Import => new ImportHandler(member)
- case DocDef(_, documented) => chooseHandler(documented)
- case member => new GenericHandler(member)
+ case member: DefDef => new DefHandler(member)
+ case member: ValDef => new ValHandler(member)
+ case member: Assign => new AssignHandler(member)
+ case member: ModuleDef => new ModuleHandler(member)
+ case member: ClassDef => new ClassHandler(member)
+ case member: TypeDef => new TypeAliasHandler(member)
+ case member: Import => new ImportHandler(member)
+ case DocDef(_, documented) => chooseHandler(documented)
+ case member => new GenericHandler(member)
}
sealed abstract class MemberDefHandler(override val member: MemberDef) extends MemberHandler(member) {
@@ -129,7 +129,7 @@ trait MemberHandlers {
}
class AssignHandler(member: Assign) extends MemberHandler(member) {
- val lhs = member.lhs.asInstanceOf[Ident] // an unfortunate limitation
+ val Assign(lhs, rhs) = member
val name = newTermName(freshInternalVarName())
override def definesTerm = Some(name)
diff --git a/test/files/run/repl-bare-expr.check b/test/files/run/repl-bare-expr.check
new file mode 100644
index 0000000000..04daa48232
--- /dev/null
+++ b/test/files/run/repl-bare-expr.check
@@ -0,0 +1,36 @@
+Type in expressions to have them evaluated.
+Type :help for more information.
+
+scala>
+
+scala> 2 ; 3
+res0: Int = 3
+
+scala> { 2 ; 3 }
+res1: Int = 3
+
+scala> 5 ; 10 ; case object Cow ; 20 ; class Moo { override def toString = "Moooooo" } ; 30 ; def bippy = {
+ 1 +
+ 2 +
+ 3 } ; bippy+88+11
+defined module Cow
+defined class Moo
+bippy: Int
+res2: Int = 105
+
+scala>
+
+scala> object Bovine { var x: List[_] = null } ; case class Ruminant(x: Int) ; bippy * bippy * bippy
+defined module Bovine
+defined class Ruminant
+res3: Int = 216
+
+scala> Bovine.x = List(Ruminant(5), Cow, new Moo)
+Bovine.x: List[Any] = List(Ruminant(5), Cow, Moooooo)
+
+scala> Bovine.x
+res4: List[Any] = List(Ruminant(5), Cow, Moooooo)
+
+scala>
+
+scala>
diff --git a/test/files/run/repl-bare-expr.scala b/test/files/run/repl-bare-expr.scala
new file mode 100644
index 0000000000..df9849fa6d
--- /dev/null
+++ b/test/files/run/repl-bare-expr.scala
@@ -0,0 +1,16 @@
+import scala.tools.partest.ReplTest
+
+object Test extends ReplTest {
+ def code = """
+2 ; 3
+{ 2 ; 3 }
+5 ; 10 ; case object Cow ; 20 ; class Moo { override def toString = "Moooooo" } ; 30 ; def bippy = {
+ 1 +
+ 2 +
+ 3 } ; bippy+88+11
+
+object Bovine { var x: List[_] = null } ; case class Ruminant(x: Int) ; bippy * bippy * bippy
+Bovine.x = List(Ruminant(5), Cow, new Moo)
+Bovine.x
+ """
+} \ No newline at end of file