Created
March 10, 2012 15:39
-
-
Save chrislewis/2011811 to your computer and use it in GitHub Desktop.
quick interpreter for nescala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package necsala.embedded | |
import scala.tools.nsc.interpreter.AbstractFileClassLoader | |
import scala.tools.nsc.{Global, Settings} | |
import scala.tools.nsc.util.BatchSourceFile | |
import scala.tools.nsc.io.{AbstractFile, VirtualDirectory} | |
import java.io.File | |
import java.util.jar.JarFile | |
import java.net.URLClassLoader | |
class Interpreter private (val classpath: List[String]) { | |
/** Evaluate a string of Scala source. Apart from compiler execution all bets are off. */ | |
def evaluate[A](code: String): A = { | |
val (name, prepared) = asInterpreterClass(code) | |
val compiler = new global.Run | |
compiler.compileSources(List(new BatchSourceFile("<text>", prepared))) | |
try { | |
val clazz = classLoader.loadClass(name) | |
val instance = clazz.newInstance() | |
val f0Any = instance.asInstanceOf[() => Any] | |
f0Any().asInstanceOf[A] | |
} catch { | |
case e => throw e //TODO coherent exception | |
} | |
} | |
/** Given raw source, generate a complete class as a subtype of () => Any. */ | |
private def asInterpreterClass(source: String): (String, String) = { | |
val name = "Interpreted_%s" format (math.abs(scala.util.Random.nextLong)) | |
name -> ("class %s extends (() => Any) { def apply() = { %s }}" format (name, source)) | |
} | |
private val target = new VirtualDirectory("<memory>", None) | |
private val classLoader = new AbstractFileClassLoader(target, this.getClass.getClassLoader) | |
private val settings = { | |
val s = new Settings() | |
s.outputDirs.setSingleOutput(target) | |
s.classpath.value = classpath.mkString(File.pathSeparator) | |
s | |
} | |
private val global = new Global(settings /*, reporter*/ ) | |
} | |
object Interpreter { | |
/** The classpath of our class loader. */ | |
def classPath: List[String] = | |
this.getClass.getClassLoader.asInstanceOf[URLClassLoader].getURLs.map(_.toString).toList | |
/** Create a new interpreter with the current classpath. */ | |
def local = new Interpreter(classPath) | |
} |
Thanks robey. I used this snippet in a workshop to demonstrate the technique, disclaiming its suitability for production in this form (I did mention to the twitter implementation). The point was merely to demonstrate the relative simplicity and appeal of the technique over other approaches.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
there's a more fleshed-out implementation of this in util-eval: https://github.com/twitter/util/blob/master/util-eval/src/main/scala/com/twitter/util/Eval.scala