During the running of a scala script, I would like it to generate some code and execute this.
I thought I had found two examples online that might work, but they aren't successful
import scala.reflect.runtime.universe._
import scala.reflect.runtime.currentMirror
import scala.tools.reflect.ToolBox
import java.io.{File, FileWriter}
def runstuff() = {
val fileWriter = new FileWriter(new File("temporaryScalaFile.scala"))
fileWriter.write("println(\"hello\")")
fileWriter.close()
temporaryScalaFile.scala
val cm = scala.reflect.runtime.universe.runtimeMirror(getClass.getClassLoader)
val tb = cm.mkToolBox()
val str = tb.eval(tb.parse("new String(\"Yo\")"))
println(str)
}
These are perhaps out of date examples.
Does anyone have a working one or a fix?
CodePudding user response:
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
- Also you can use standard Scala 2 REPL interpreter
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
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
- 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
orscala3-compiler
in your classpath you will run Scala 2 or Scala 3 (one of the two above script engines: Scala 2scala.tools.nsc.interpreter.shell.Scripted
or Scala 3dotty.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
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)
(new global.Run).compileSources(List(new BatchSourceFile("(inline)", code)))
}
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
Scala reflection: compile akka actor with protobuf
Dynamic compilation of multiple Scala classes at runtime
Tensorflow in Scala reflection
Scala Presentation Compiler - Minimal Example