Skip to content

Commit 108756f

Browse files
committed
Merge branch 'repl-bindings' into embedded-repl-bindings
# Conflicts: # repl/src/dotty/tools/repl/ReplMain.scala # repl/test/dotty/tools/repl/ReplMainTest.scala
2 parents 0caff87 + 8ec198f commit 108756f

File tree

4 files changed

+144
-4
lines changed

4 files changed

+144
-4
lines changed

repl/src/dotty/tools/repl/ReplDriver.scala

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,9 +171,12 @@ class ReplDriver(settings: Array[String],
171171
* observable outside of the CLI, for this reason, most helper methods are
172172
* `protected final` to facilitate testing.
173173
*/
174-
def runUntilQuit(using initialState: State = initialState)(): State = {
174+
def runUntilQuit(using initialState: State = initialState)(hardcodedInput: java.io.InputStream = null): State = {
175175
val terminal = new JLineTerminal
176176

177+
val hardcodedInputLines =
178+
if (hardcodedInput == null) null
179+
else new java.io.BufferedReader(new java.io.InputStreamReader(hardcodedInput))
177180
out.println(
178181
s"""Welcome to Scala $simpleVersionString ($javaVersion, Java $javaVmName).
179182
|Type in expressions for evaluation. Or try :help.""".stripMargin)
@@ -211,8 +214,12 @@ class ReplDriver(settings: Array[String],
211214
}
212215

213216
try {
214-
val line = terminal.readLine(completer)
215-
ParseResult(line)
217+
val line =
218+
if (hardcodedInputLines != null) hardcodedInputLines.readLine()
219+
else terminal.readLine(completer)
220+
221+
if (line == null) Quit
222+
else ParseResult(line)
216223
} catch {
217224
case _: EndOfFileException => // Ctrl+D
218225
Quit
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package dotty.tools.repl
2+
3+
import java.io.PrintStream
4+
5+
class ReplMain(
6+
settings: Array[String] = Array.empty,
7+
out: PrintStream = Console.out,
8+
classLoader: Option[ClassLoader] = Some(getClass.getClassLoader),
9+
predefCode: String = "",
10+
testCode: String = ""
11+
):
12+
def run(bindings: ReplMain.Bind[_]*): Any =
13+
try
14+
ReplMain.currentBindings.set(bindings.map{bind => bind.name -> bind.value}.toMap)
15+
16+
val bindingsPredef = bindings
17+
.map { case bind =>
18+
s"def ${bind.name}: ${bind.typeName.value} = dotty.tools.repl.ReplMain.currentBinding[${bind.typeName.value}](\"${bind.name}\")"
19+
}
20+
.mkString("\n")
21+
22+
val fullPredef =
23+
ReplDriver.pprintImport +
24+
(if bindingsPredef.nonEmpty then s"\n$bindingsPredef\n" else "") +
25+
(if predefCode.nonEmpty then s"\n$predefCode\n" else "")
26+
27+
val driver = new ReplDriver(settings, out, classLoader, fullPredef)
28+
29+
if (testCode == "") driver.tryRunning
30+
else driver.runUntilQuit(using driver.initialState)(
31+
new java.io.ByteArrayInputStream(testCode.getBytes())
32+
)
33+
()
34+
finally
35+
ReplMain.currentBindings.set(null)
36+
37+
38+
object ReplMain:
39+
final case class TypeName[A](value: String)
40+
object TypeName extends TypeNamePlatform
41+
42+
import scala.quoted._
43+
44+
trait TypeNamePlatform:
45+
inline given [A]: TypeName[A] = ${TypeNamePlatform.impl[A]}
46+
47+
object TypeNamePlatform:
48+
def impl[A](using t: Type[A], ctx: Quotes): Expr[TypeName[A]] =
49+
'{TypeName[A](${Expr(Type.show[A])})}
50+
51+
52+
case class Bind[T](name: String, value: () => T)(implicit val typeName: TypeName[T])
53+
object Bind:
54+
implicit def ammoniteReplArrowBinder[T](t: (String, T))(implicit typeName: TypeName[T]): Bind[T] = {
55+
Bind(t._1, () => t._2)(typeName)
56+
}
57+
58+
def currentBinding[T](s: String): T = currentBindings.get().apply(s).apply().asInstanceOf[T]
59+
60+
private val currentBindings = new ThreadLocal[Map[String, () => Any]]()
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package dotty.tools
2+
package repl
3+
4+
import scala.language.unsafeNulls
5+
6+
import java.io.{ByteArrayOutputStream, PrintStream}
7+
import java.nio.charset.StandardCharsets
8+
9+
import vulpix.TestConfiguration
10+
import org.junit.Test
11+
import org.junit.Assert._
12+
13+
/** Tests for the programmatic REPL API (ReplMain) */
14+
class ReplMainTest:
15+
16+
private val defaultOptions = Array("-classpath", TestConfiguration.withCompilerClasspath)
17+
18+
private def captureOutput(body: PrintStream => Unit): String =
19+
val out = new ByteArrayOutputStream()
20+
val ps = new PrintStream(out, true, StandardCharsets.UTF_8.name)
21+
body(ps)
22+
dotty.shaded.fansi.Str(out.toString(StandardCharsets.UTF_8.name)).plainText
23+
24+
@Test def basicBinding(): Unit =
25+
val output = captureOutput { out =>
26+
val replMain = new ReplMain(
27+
settings = defaultOptions,
28+
out = out,
29+
testCode = "test"
30+
)
31+
32+
replMain.run("test" -> 42)
33+
}
34+
35+
assertTrue(output.contains("val res0: Int = 42"))
36+
37+
@Test def multipleBindings(): Unit =
38+
val output = captureOutput { out =>
39+
val replMain = new ReplMain(
40+
settings = defaultOptions,
41+
out = out,
42+
testCode = "x\ny\nz"
43+
)
44+
45+
replMain.run(
46+
"x" -> 1,
47+
"y" -> "hello",
48+
"z" -> true
49+
)
50+
}
51+
52+
assertTrue(output.contains("val res0: Int = 1"))
53+
assertTrue(output.contains("val res1: String = \"hello\""))
54+
assertTrue(output.contains("val res2: Boolean = true"))
55+
56+
@Test def bindingTypes(): Unit =
57+
val output = captureOutput { out =>
58+
val replMain = new ReplMain(
59+
settings = defaultOptions ++ Array("-repl-quit-after-init"),
60+
out = out,
61+
testCode = "list\nmap"
62+
)
63+
64+
replMain.run(
65+
"list" -> List(1, 2, 3),
66+
"map" -> Map(1 -> "hello")
67+
)
68+
}
69+
70+
assertTrue(output.contains("val res0: List[Int] = List(1, 2, 3)"))
71+
assertTrue(output.contains("val res1: Map[Int, String] = Map(1 -> \"hello\")"))
72+
73+
end ReplMainTest

sbt-bridge/src-bootstrapped/ConsoleInterface.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public void run(
4949

5050
state = driver.run(initialCommands, state);
5151
// TODO handle failure during initialisation
52-
state = driver.runUntilQuit(state);
52+
state = driver.runUntilQuit(state, null);
5353
driver.run(cleanupCommands, state);
5454
}
5555
}

0 commit comments

Comments
 (0)