2016-06-10 8 views
5

Neu bei Scala und nach Hinweisen auf eine idiomatische Lösung suchen, falls es welche gibt.Wie kompiliere/evaluiere ich einen Scala-Ausdruck zur Laufzeit?

Ich möchte, dass beliebige benutzerdefinierte Scala-Funktionen (die Funktionen/Klassen verweisen können, die ich in meinem Code definiert habe) auf einige Daten angewendet werden.

Zum Beispiel: Ich habe foo(s: String): String und bar(s: String): String Funktionen in meiner myprog.scala definiert. Der Benutzer führt mein Programm wie folgt aus:

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

Diese Zeile für Zeile durch die Datendatei laufen würde und das Ergebnis der Anwendung der vom Benutzer festgelegten Funktion zu dieser Linie emittieren.

Kann ich für zusätzliche Punkte sicherstellen, dass keine Nebenwirkungen in der benutzerdefinierten Funktion auftreten? Wenn nicht, kann ich die Funktion darauf beschränken, nur eine eingeschränkte Teilmenge von Funktionen zu verwenden (die ich versichern kann)?

+0

Ich würde sehen, wie das Lambdabot auf freenode geschrieben wird, klingt Ihr Problem ähnlich. – Reactormonk

Antwort

2

@kenjiyoshida hat eine nette gist, die zeigt, wie Scala-Code auszuwerten. Beachten Sie, dass bei der Verwendung von Eval aus dieser Liste kein Rückgabewert zu einem Laufzeitfehler führt, wenn Scala standardmäßig auf Nothing verweist.

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 

Es behandelt schön, was auch in ihrem Umfang ist.

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 
} 

util Pakets Twitter verwendet util-eval haben, aber das scheint veraltet sind jetzt (und auch löst einen Fehler Compiler beim Kompilieren).

Wie für den zweiten Teil Ihrer Frage scheint die Antwort nein zu sein. Selbst wenn Sie die Standardeinstellung Predef deaktivieren und sich selbst importieren, kann ein Benutzer diese Funktionen immer mit dem vollständig qualifizierten Paketnamen aufrufen. Sie könnten vielleicht Scalas scala.tools.reflect.ToolBox verwenden, um zuerst Ihre Zeichenfolge zu parsen und dann mit einer Whitelist zu vergleichen, bevor Sie an eval übergeben werden, aber an diesem Punkt könnten die Dinge ziemlich haarig werden, da Sie manuell Code schreiben werden, um die Scala AST zu desinfizieren am wenigsten gefährliche Eingabe zurückweisen). Es scheint definitiv keine "idiomatische Lösung" zu sein.

0

unter Verwendung des Standard-Java JSR 223 Scripting Engine

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

(auch Nennungen mit scala.tools.nsc.Interpreter aber nicht sicher, ob diese noch verfügbar ist)

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(); 
} 
möglich sein Dies sollte
Verwandte Themen