Scala 2.10的嵌入式REPL解释器示例

18
请给出一个代码示例,展示如何以编程方式创建一个嵌入式Scala REPL解释器,并使其在Scala 2.10中运行。(我在挖掘各种代码片段几个小时后才得到一个可用的解释器,因此添加了这个问题和答案)
2个回答

23

示例 Repl.scala

import scala.tools.nsc.interpreter._
import scala.tools.nsc.Settings


object Repl extends App {
  def repl = new ILoop {
    override def loop(): Unit = {
      intp.bind("e", "Double", 2.71828)
      super.loop()
    }
  }

  val settings = new Settings
  settings.Yreplsync.value = true


  //use when launching normally outside SBT
  settings.usejavacp.value = true      

  //an alternative to 'usejavacp' setting, when launching from within SBT
  //settings.embeddedDefaults[Repl.type]

  repl.process(settings)
}

一些注释

  • 我选择展示JLineReader (默认值) 而非SimpleReader, 因为它能更好地运作, 正确处理箭头键、删除等操作。但使用JLine需要添加一个jar依赖。
  • 该示例展示了如何将值绑定到repl中 (上面的变量 e)。
  • 如果省略settings.Yreplsync.value = true, repl会挂起,变得毫无用处。
  • 通过我的测试,如果同时将usejavacpembeddedDefaults设置结合在一起,会导致出现错误。

我发现最简单的测试是通过SBT进行; 一个示例build.sbt:

name := "Repl"

organization := "ExamplesRUs"

scalaVersion := "2.10.2"

libraryDependencies ++= Seq(
 "org.scala-lang" % "scala-compiler" % "2.10.2",
 "org.scala-lang" % "jline" % "2.10.2"
)

示例 SBT 会话:

> run-main Repl
[info] Running Repl
Welcome to Scala version 2.10.2 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_37).
Type in expressions to have them evaluated.
Type :help for more information.
e: Double = 2.71828

scala> 2 * e
res1: Double = 5.43656

scala>

我回答过这个问题 http://stackoverflow.com/a/18418634/1296806 但我不认为我试过2.10版本,而且我总是忘记-Yrepl-sync选项。我也没有使用sbt控制台。谢谢! - som-snytt
1
还有一个关于绑定值和类加载器的问题,链接为http://stackoverflow.com/a/18503457/1296806。我无法确定它是否是好信息,因为它被忽略了。当你需要它时,众人的想法在哪里? - som-snytt
@som-snytt,是的,你的回答是这个例子中最有用的来源。 - Ben Hutchison
5
无法与 Scala 2.11 兼容,因为“method loop cannot override final method”。 - simbo1905
2
@simbo1905 对于2.11版本,不要覆盖createInterpreter方法,只需在调用intp.bind之前调用super().createInterpreter()即可。 - Clark Updike
如何将所有本地作用域绑定到REPL?(在最新版本中) - HappyFace

4
基于Ben的优秀回答,下面是一个助手类来方便启动解释器。 用法:
Repl.run(("e", "Double", 2.71828), ("pi", "Double", 3.1415))

它可以自动检测您是否正在使用SBT,并做出相应调整。

Repl.scala:

import scala.tools.nsc.interpreter.ILoop
import scala.tools.nsc.Settings
import java.io.CharArrayWriter
import java.io.PrintWriter

object Repl {

  def run(params: (String, String, Any)*) {

    def repl = new ILoop {
      override def loop(): Unit = {
        params.foreach(p => intp.bind(p._1, p._2, p._3))
        super.loop()
      }
    }

    val settings = new Settings
    settings.Yreplsync.value = true

    // Different settings needed when running from SBT or normally
    if (isRunFromSBT) {
      settings.embeddedDefaults[Repl.type]
    } else {
      settings.usejavacp.value = true
    }

    repl.process(settings)
  }

  def isRunFromSBT = {
    val c = new CharArrayWriter()
    new Exception().printStackTrace(new PrintWriter(c))
    c.toString().contains("at sbt.")
  }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接