Skip to content

Instantly share code, notes, and snippets.

@chrislewis
Created March 10, 2012 15:39
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save chrislewis/2011811 to your computer and use it in GitHub Desktop.
Save chrislewis/2011811 to your computer and use it in GitHub Desktop.
quick interpreter for nescala
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)
}
@robey
Copy link

robey commented Mar 16, 2012

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

@chrislewis
Copy link
Author

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