How to compile / eval Scala expression at runtime?

New to Scala and looking for pointers to an idiomatic solution, if any.

I would like to have arbitrary Scala custom functions (which are allowed to refer to functions / classes that I defined in my code) applied to some data.

For example: I have foo(s: String): String and bar(s: String): String myprog.scala defined in my myprog.scala . A user launches my program as follows:

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

This will be done line by line through the data file and emit the result of applying the function specified by the user to that line.

For extra points, can I make sure there are no side effects in the user-defined function? If not, can I restrict a function to using only a limited subset of functions (what can I guarantee to be safe)?

+5
source share
2 answers

@kenjiyoshida has a nice gist that shows how to define Scala code. Note that if you use Eval from this value without specifying a return value, it will crash at runtime when Scala defaults to 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 

It does an excellent job of being in scope.

  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 in the util-eval package, but this seems to be deprecated now (and also causes a compiler error when compiling).

As for the second part of your question, the answer seems no. Even if you disable Predef by default and import yourself, the user can always get these functions with the full package name. You could use Scala scala.tools.reflect.ToolBox to first scala.tools.reflect.ToolBox your string and then compare with a scala.tools.reflect.ToolBox before moving on to Eval , but at this point everything can get pretty hairy, as you will manually write code for Scala AST disinfection (or at least reject a dangerous entrance). This is definitely not like an "idiomatic solution."

+3
source

This should be possible using the standard Java JSR 223 Scripting Engine.

see https://issues.scala-lang.org/browse/SI-874

(also mentions using scala.tools.nsc.Interpreter, but not sure if this is still available)

 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(); } 
0
source

All Articles