如何在运行时编译/评估Scala表达式?

时间:2016-06-10 22:21:31

标签: scala

Scala的新手,并寻找指向惯用解决方案的指针,如果有的话。

我希望将一些用户提供的Scala函数(允许引用我在代码中定义的函数/类)应用于某些数据。

例如:我在foo(s: String): String中定义了bar(s: String): Stringmyprog.scala个函数。用户运行我的程序:

$ scala myprog data.txt --func='(s: Str) => foo(bar(s)).reverse'

这将逐行遍历数据文件并发出将用户指定的函数应用于该行的结果。

对于额外的积分,我可以确保用户定义的功能中没有副作用吗?如果没有,我可以限制功能只使用 一个有限的功能子集(我可以保证安全)吗?

2 个答案:

答案 0 :(得分:4)

@kenjiyoshida有一个很好的gist,它展示了如何评估Scala代码。请注意,当使用该gist中的Eval时,如果Scala默认推断Nothing,则不指定返回值将导致运行时失败。

scala> Eval("println(\"Hello\")")
Hello
java.lang.ClassCastException: scala.runtime.BoxedUnit cannot be cast to scala.runtime.Nothing$
  ... 42 elided

VS

scala> Eval[Unit]("println(\"Hello\")")
Hello

它很好地处理范围内的任何内容。

 object Thing {
   val thing: Int = 5
 }

 object Eval {

   def apply[A](string: String): A = {
     val toolbox = currentMirror.mkToolBox()
     val tree = toolbox.parse(string)
     toolbox.eval(tree).asInstanceOf[A]
   }

   def fromFile[A](file: File): A =
     apply(scala.io.Source.fromFile(file).mkString(""))

   def fromFileName[A](file: String): A =
     fromFile(new File(file))

 }

 object Thing2 {
   val thing2 = Eval[Int]("Thing.thing") // 5
 }

Twitter的util包曾经有util-eval,但现在似乎已被弃用(并且在编译时也会触发编译器错误)。

关于你问题的第二部分,答案似乎是否定的。即使您禁用默认Predef并自行导入,用户也可以始终使用完全限定的包名称来访问这些函数。您可以使用Scala的scala.tools.reflect.ToolBox来首先解析您的字符串,然后在传递给eval之前与白名单进行比较,但此时事情可能变得非常毛茸茸,因为您将手动编写代码以进行清理Scala AST(或者至少拒绝危险的输入)。它绝对不是一个“惯用的解决方案。”

答案 1 :(得分:0)

这应该可以通过使用标准Java JSR 223脚本引擎

来实现

请参阅https://issues.scala-lang.org/browse/SI-874

(也提到使用scala.tools.nsc.Interpreter但不确定这仍然可用)

import javax.script.*;
ScriptEngine e = new ScriptEngineManager().getEngineByName("scala");
e.getContext().setAttribute("label", new Integer(4), ScriptContext.ENGINE_SCOPE);
try {
    engine.eval("println(2+label)");
} catch (ScriptException ex) {
    ex.printStackTrace();
}