2016-06-10 81 views
5

斯卡拉新手,尋找指向一個慣用解決方案的指針,如果有的話。如何在運行時編譯/評估Scala表達式?

我想要任意用戶提供的Scala函數(可以引用我在代碼中定義的函數/類)應用於某些數據。

例如:我有我的myprog.scala中定義的foo(s: String): Stringbar(s: String): String函數。用戶運行我的程序是這樣的:

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

這將逐行通過數據文件運行,併發出將用戶指定的功能到該行的結果。

對於額外的問題,我可以確保用戶定義函數中沒有副作用嗎?如果沒有,我可以限制功能只使用功能的一個受限子集(我可以保證是安全的)?

+0

我會看看freenode上的lambdabot是如何編寫的,你的問題聽起來很相似。 – Reactormonk

回答

2

@kenjiyoshida有一個不錯的gist,它展示瞭如何評估Scala代碼。請注意,從該要點使用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-evalutil包,但似乎已經被現在已經過時(編譯時,也觸發編譯器錯誤)。

至於你的問題的第二部分,答案似乎是否定的。即使您禁用默認的Predef並自行導入,用戶也可以始終使用完全限定的包名稱來訪問這些函數。你也許可以使用Scala的scala.tools.reflect.ToolBox首先解析你的字符串,然後再與eval進行比較,然後在那個時候事情會變得非常多毛,因爲你會手動編寫代碼來清理Scala AST(或者在至少拒絕危險的輸入)。它絕對不是一個「慣用的解決方案」。

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