I'll adopt to Scala 2 my answer in How to compile and execute scala code at run-time in Scala3?
ammonite.Main(verboseOutput = false).runCode("""println("Hello, World!")""")
// Hello, World!
build.sbt
scalaVersion := "2.13.8"
libraryDependencies += "com.lihaoyi" % "ammonite" % "2.5.4" cross CrossVersion.full
scala.tools.nsc.interpreter.shell.Scripted()
.eval("""System.out.println("Hello, World!")""")
// Hello, World!
build.sbt
scalaVersion := "2.13.8"
libraryDependencies ++= scalaOrganization.value % "scala-compiler" % scalaVersion.value
- You can use Scala 2 reflective Toolbox
val tb = scala.tools.reflect.ToolBox(scala.reflect.runtime.currentMirror).mkToolBox()
tb.eval(tb.parse("""println("Hello, world!")"""))
// Hello, world!
or
import scala.tools.reflect.ToolBox // implicit
val tb = scala.reflect.runtime.currentMirror.mkToolBox()
tb.eval(tb.parse("""println("Hello, world!")"""))
// Hello, world!
build.sbt
scalaVersion := "2.13.8"
libraryDependencies ++= scalaOrganization.value % "scala-compiler" % scalaVersion.value
- You can use
scala.tools.nsc.interpreter
val settings = new scala.tools.nsc.Settings
settings.usejavacp.value = true
new scala.tools.nsc.interpreter.IMain(settings,
new scala.tools.nsc.interpreter.shell.ReplReporterImpl(settings)
).interpret("""println("Hello, World!")""")
// Hello, World!
build.sbt
scalaVersion := "2.13.8"
libraryDependencies ++= scalaOrganization.value % "scala-compiler" % scalaVersion.value
- If you have a
scala.reflect.runtime.universe.Tree
q"..."
rather than plain string then you don't need to parse
import scala.reflect.runtime.universe.Quasiquote // implicit for q"..." interpolator
import scala.tools.reflect.ToolBox // implicit for .eval
scala.reflect.runtime.currentMirror.mkToolBox()
.eval(q"""println("Hello, World!")""") // Hello, World!
build.sbt
scalaVersion := "2.13.8"
libraryDependencies ++= scalaOrganization.value % "scala-compiler" % scalaVersion.value
- If you have a
scala.reflect.runtime.universe.Expr
reify {...}
(a statically typed wrapper over a tree) rather than plain string then you also don't need to parse
import scala.reflect.runtime.universe.reify
import scala.tools.reflect.ToolBox
scala.reflect.runtime.currentMirror.mkToolBox()
.eval(reify{ println("Hello, World!") }.tree)
// Hello, World!
build.sbt
scalaVersion := "2.13.8"
libraryDependencies ++= scalaOrganization.value % "scala-compiler" % scalaVersion.value
- All of the above is to run Scala 2 code in Scala 2. If we want to run Scala 3 code in Scala 2 then we can use standard Scala 3 REPL interpreter
(new dotty.tools.repl.ScriptEngine).eval("""println("Hello, World!")""")
// Hello, World!
build.sbt
scalaVersion := "2.13.8"
libraryDependencies += scalaOrganization.value %% "scala3-compiler" % "3.1.3" cross CrossVersion.for2_13Use3
scalacOptions += "-Ytasty-reader"
- Also you can use JSR223 scripting. Depending on whether you have
scala-compiler
or scala3-compiler
in your classpath you will run Scala 2 or Scala 3 (one of the two above script engines: Scala 2 scala.tools.nsc.interpreter.shell.Scripted
or Scala 3 dotty.tools.repl.ScriptEngine
). If you have both the dependency added first wins.
new javax.script.ScriptEngineManager(getClass.getClassLoader)
.getEngineByName("scala")
.eval("""println("Hello, World!")""")
// Hello, World!
If you'd like to have a better control what dependency is used (without re-importing the project) you can use Coursier and specify class loader
import coursier._ // libraryDependencies += "io.get-coursier" %% "coursier" % "2.1.0-M6-53-gb4f448130"
val files = Fetch()
.addDependencies(
Dependency(Module(Organization("org.scala-lang"), ModuleName("scala-compiler")), "2.13.9"),
// Dependency(Module(Organization("org.scala-lang"), ModuleName("scala3-compiler_3")), "3.2.0"),
)
.run()
val classLoader = new java.net.URLClassLoader(
files.map(_.toURI.toURL).toArray,
/*getClass.getClassLoader*/null // ignoring current classpath
)
new javax.script.ScriptEngineManager(classLoader)
.getEngineByName("scala")
.eval("""
type T = List[Option[A]] forSome {type A} // Scala 2
//type T = [A] =>> [B] =>> (A, B) // Scala 3
System.out.println("Hello, World!")
""")
// Hello, World!
- You can implement Eval in Scala 2 yourself using actual compiler
import scala.reflect.internal.util.{AbstractFileClassLoader, BatchSourceFile}
import scala.reflect.io.{AbstractFile, VirtualDirectory}
import scala.reflect.runtime.universe
import scala.reflect.runtime
import scala.reflect.runtime.universe.{Mirror, TermName}
import scala.tools.nsc.{Global, Settings}
val code =
s"""
|package mypackage
|
|object Main {
| def main(args: Array[String]): Unit = {
| println("Hello, World!")
| }
|}""".stripMargin
val directory = new VirtualDirectory("(memory)", None)
val runtimeMirror = createRuntimeMirror(directory, runtime.currentMirror)
compileCode(code, List(), directory)
runObjectMethod("mypackage.Main", runtimeMirror, "main", Array.empty[String])
// Hello, World!
def compileCode(
code: String,
classpathDirectories: List[AbstractFile],
outputDirectory: AbstractFile
): Unit = {
val settings = new Settings
classpathDirectories.foreach(dir => settings.classpath.prepend(dir.toString))
settings.outputDirs.setSingleOutput(outputDirectory)
settings.usejavacp.value = true
val global = new Global(settings)
val run = new global.Run
run.compileSources(List(new BatchSourceFile("(inline)", code)))
// val unit = run.units.next()
// println("source=" + unit.source.content.mkString)
// println("typed tree=" + unit.body)
}
def runObjectMethod(
objectName: String,
runtimeMirror: Mirror,
methodName: String,
arguments: Any*
): Any = {
val objectSymbol = runtimeMirror.staticModule(objectName)
val objectModuleMirror = runtimeMirror.reflectModule(objectSymbol)
val objectInstance = objectModuleMirror.instance
val objectType = objectSymbol.typeSignature
val methodSymbol = objectType.decl(TermName(methodName)).asMethod
val objectInstanceMirror = runtimeMirror.reflect(objectInstance)
val methodMirror = objectInstanceMirror.reflectMethod(methodSymbol)
methodMirror(arguments: _*)
}
def createRuntimeMirror(directory: AbstractFile, parentMirror: Mirror): Mirror = {
val classLoader = new AbstractFileClassLoader(directory, parentMirror.classLoader)
universe.runtimeMirror(classLoader)
}
build.sbt
scalaVersion := "2.13.8"
libraryDependencies ++= scalaOrganization.value % "scala-compiler" % scalaVersion.value
- Also Scalameta Semanticdb has method
scala.meta.interactive.InteractiveSemanticdb.newCompiler()
returning a scala.tools.nsc.interactive.Global
extending above scala.tools.nsc.Global
.
build.sbt
scalaVersion := "2.13.8"
// adding as a dependency, not a compiler plugin
libraryDependencies += "org.scalameta" % "semanticdb-scalac" % "4.6.0" cross CrossVersion.full
Scala reflection: compile akka actor with protobuf
Dynamic compilation of multiple Scala classes at runtime
How to eval code that uses InterfaceStability annotation (that fails with "illegal cyclic reference involving class InterfaceStability")?
Tensorflow in Scala reflection
Scala Presentation Compiler - Minimal Example
What is "Scala Presentation Compiler"?
"eval" in Scala