How to compile and execute scala code at run-time in Scala3?

Viewed 247

I would like to compile and execute Scala code given as a String at run-time using Scala3. Like for example in Scala 2 I would have used Reflection

import scala.reflect.runtime.universe as ru
import scala.tools.reflect.ToolBox
val scalaCode = q"""println("Hello world!")"""
val evalMirror = ru.runtimeMirror(this.getClass.getClassLoader)
val toolBox = evalMirror.mkToolBox()
toolBox.eval(scalaCode) //Hello world!

If I try to run this code in Scala3 I get

Scala 2 macro cannot be used in Dotty. See https://dotty.epfl.ch/docs/reference/dropped-features/macros.html
To turn this error into a warning, pass -Xignore-scala2-macros to the compiler

How can I translate this code in Scala3 ?

1 Answers
ammonite.Main(verboseOutput = false).runCode("""println("Hello world!")""")
// Hello world!

build.sbt

scalaVersion := "3.1.3"
libraryDependencies += "com.lihaoyi" % "ammonite" % "2.5.4-22-4a9e6989" cross CrossVersion.full
excludeDependencies ++= Seq(
  ExclusionRule("com.lihaoyi", "sourcecode_2.13"),
  ExclusionRule("com.lihaoyi", "fansi_2.13"),
)
com.eed3si9n.eval.Eval()
  .evalInfer("""println("Hello, World!")""")
  .getValue(this.getClass.getClassLoader)
// Hello, World!

build.sbt

scalaVersion := "3.1.1"
libraryDependencies += "com.eed3si9n.eval" % "eval" % "0.1.0" cross CrossVersion.full
new javax.script.ScriptEngineManager(getClass.getClassLoader)
  .getEngineByName("scala")
  .eval("""println("Hello, World!")""")
// Hello, World!
  • If you have an Expr (a wrapper over a tree) rather than string then you can use runtime multi-staging
import scala.quoted.*
given staging.Compiler = staging.Compiler.make(getClass.getClassLoader)
staging.run('{ println("Hello, World!") }) // Hello, World!

build.sbt

scalaVersion := "3.1.3"
libraryDependencies += scalaOrganization.value %% "scala3-staging" % scalaVersion.value
  • All of the above is to run Scala 3 code in Scala 3. If we want to run Scala 2 code in Scala 3 then we can still use reflective Toolbox. Scala 2 macros don't work, so we can't do runtime.currentMirror or q"..." but can do universe.runtimeMirror or tb.parse
import scala.tools.reflect.ToolBox
import scala.reflect.runtime.universe

val rm = universe.runtimeMirror(getClass.getClassLoader)
val tb = rm.mkToolBox()
tb.eval(tb.parse("""println("Hello world!")"""))
// Hello world!

build.sbt

scalaVersion := "3.1.3"
libraryDependencies ++= Seq(
  scalaOrganization.value % "scala-reflect" % "2.13.8",
  scalaOrganization.value % "scala-compiler" % "2.13.8"
)

See also

https://www.chris-kipp.io/blog/an-intro-to-the-scala-presentation-compiler

Parsing scala 3 code from a String into Scala 3 AST at runtime

https://www.reddit.com/r/scala/comments/siatmj/scala_3_reflection/

Related