[Scala]Scalaでeval

Rubyのように文字列をScalaスクリプトとしてプログラム内でevalする方法。

scala-compiler.jarの中にscalaインタプリタが使っているクラスInterpreter(そのまんま)があるので、これでevalできる。

package jp.hogehoge

import scala.tools.nsc._
import scala.util.DynamicVariable

class Evaluator {
  val i = new Interpreter(new Settings(null))

  def eval(line: String): Any = {
    i.interpret("jp.hogehoge.Evaluator.result.value = " + line)
    Evaluator.result.value
  }
}

object Evaluator {
  val result = new DynamicVariable[Any](())
}

object EvalTest {
  def main(args: Array[String]): Unit = {
    val e = new Evaluator
    val r = e.eval("jp.hogehoge.Hoge.somefunc")
    println(r)
  }
}

object Hoge {
  def somefunc = "mogemoge"
}

解説

  • Interpreter::interpretは式の評価値を返してくれない。
    • なので、グローバルな変数Evaluator.resultに一旦結果を代入するコードをeval文字列の中に追加する。
    • Evaluatorがマルチスレッドで使われる時に備えて、Evaluator.resultをDynamicVariableにしておく。
  • 注意点
    • ひとつinterpretするだけで平均30ミリ秒くらいはかかるので、ループの中でぶん回すのはやめたほうがいいと思う。
    • クラスパスにあるものは何でも実行できる諸刃の剣なので、サニタイズを忘れずに。