use*_*509 5 java macros scala scala-macros scala-3
我想使用 Scala3 编译并执行在运行时以字符串形式给出的 Scala 代码。例如在 Scala 2 中我会使用 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!
Run Code Online (Sandbox Code Playgroud)
如果我尝试在 Scala3 中运行这段代码,我会得到
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
Run Code Online (Sandbox Code Playgroud)
我如何在 Scala3 中翻译这段代码?
Dmy*_*tin 11
这个答案的 Scala 2 版本在这里:How can I run generated code during script running?
在 Scala 3 中:
ammonite.Main(verboseOutput = false).runCode("""println("Hello, World!")""")
// Hello, World!
Run Code Online (Sandbox Code Playgroud)
构建.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"),
)
Run Code Online (Sandbox Code Playgroud)
com.eed3si9n.eval.Eval()
.evalInfer("""println("Hello, World!")""")
.getValue(this.getClass.getClassLoader)
// Hello, World!
Run Code Online (Sandbox Code Playgroud)
构建.sbt
scalaVersion := "3.2.0"
libraryDependencies += "com.eed3si9n.eval" % "eval" % "0.1.0" cross CrossVersion.full
Run Code Online (Sandbox Code Playgroud)
com.github.dmytromitin.eval.Eval[Unit]("""println("Hello, World!")""")
// Hello, World!
Run Code Online (Sandbox Code Playgroud)
scalaVersion := "3.2.1"
libraryDependencies += "com.github.dmytromitin" %% "eval" % "0.1"
Run Code Online (Sandbox Code Playgroud)
dotty.tools.repl.ScriptEngine().eval("""println("Hello, World!")""")
// Hello, World!
Run Code Online (Sandbox Code Playgroud)
构建.sbt
scalaVersion := "3.1.3"
libraryDependencies += scalaOrganization.value %% "scala3-compiler" % scalaVersion.value
Run Code Online (Sandbox Code Playgroud)
scala.quoted.Expr '{...}(抽象语法树上的静态类型包装器scala.quoted.Quotes#Tree)而不是纯字符串,那么您可以使用运行时多阶段scalaVersion := "3.1.3"
libraryDependencies += scalaOrganization.value %% "scala3-compiler" % scalaVersion.value
Run Code Online (Sandbox Code Playgroud)
构建.sbt
import scala.quoted.*
given staging.Compiler = staging.Compiler.make(getClass.getClassLoader)
staging.run('{ println("Hello, World!") })
// Hello, World!
Run Code Online (Sandbox Code Playgroud)
runtime.currentMirrororq"..."但可以做universe.runtimeMirrorortb.parsescalaVersion := "3.1.3"
libraryDependencies += scalaOrganization.value %% "scala3-staging" % scalaVersion.value
Run Code Online (Sandbox Code Playgroud)
构建.sbt
import scala.tools.reflect.ToolBox // implicit
val tb = scala.reflect.runtime.universe
.runtimeMirror(getClass.getClassLoader)
.mkToolBox()
tb.eval(tb.parse("""println("Hello, World!")"""))
// Hello, World!
Run Code Online (Sandbox Code Playgroud)
scalaVersion := "3.1.3"
libraryDependencies ++= scalaOrganization.value % "scala-compiler" % "2.13.8"
Run Code Online (Sandbox Code Playgroud)
构建.sbt
scala.tools.nsc.interpreter.shell.Scripted()
.eval("""System.out.println("Hello, World!")""")
// Hello, World!
Run Code Online (Sandbox Code Playgroud)
scala3-compiler或scala-compiler在您的类路径中,您将运行 Scala 3 或 Scala 2 (上述两个脚本引擎之一: Scala 3dotty.tools.repl.ScriptEngine或 Scala 2 scala.tools.nsc.interpreter.shell.Scripted)。如果您首先添加了两个依赖项,则获胜。scalaVersion := "3.1.3"
libraryDependencies ++= scalaOrganization.value % "scala-compiler" % "2.13.8"
Run Code Online (Sandbox Code Playgroud)
如果您想更好地控制使用什么依赖项(无需重新导入项目),您可以使用 Coursier 并指定类加载器
new javax.script.ScriptEngineManager(getClass.getClassLoader)
.getEngineByName("scala")
.eval("""println("Hello, World!")""")
// Hello, World!
Run Code Online (Sandbox Code Playgroud)
import coursier.* // libraryDependencies += "io.get-coursier" %% "coursier" % "2.1.0-M6-53-gb4f448130" cross CrossVersion.for3Use2_13
val files = Fetch()
.addDependencies(
Dependency(Module(Organization("org.scala-lang"), ModuleName("scala3-compiler_3")), "3.2.0"),
// Dependency(Module(Organization("org.scala-lang"), ModuleName("scala-compiler")), "2.13.9")
)
.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 = [A] =>> [B] =>> (A, B) // Scala 3
//type T = List[Option[A]] forSome {type A} // Scala 2
System.out.println("Hello, World!")
""")
// Hello, World!
Run Code Online (Sandbox Code Playgroud)
(以前的版本)
构建.sbt
import dotty.tools.io.AbstractFile
import dotty.tools.dotc.core.Contexts.Context
import dotty.tools.dotc.Driver
import dotty.tools.dotc.util.SourceFile
import dotty.tools.io.{VirtualDirectory, VirtualFile}
import java.net.URLClassLoader
import java.nio.charset.StandardCharsets
import dotty.tools.repl.AbstractFileClassLoader
import scala.io.Codec
import coursier.{Dependency, Module, Organization, ModuleName, Fetch}
// we apply usejavacp=true instead
// val files = Fetch()
// .addDependencies(
// Dependency(Module(Organization("org.scala-lang"), ModuleName("scala3-compiler_3")), "3.1.3"),
// )
// .run()
//
// val depClassLoader = new URLClassLoader(
// files.map(_.toURI.toURL).toArray,
// /*getClass.getClassLoader*/ null // ignoring current classpath
// )
val code =
s"""
|package mypackage
|
|object Main {
| def main(args: Array[String]): Unit = {
| println("Hello, World!")
| }
|}""".stripMargin
val outputDirectory = VirtualDirectory("(memory)")
compileCode(code, List()/*files.map(f => AbstractFile.getFile(f.toURI.toURL.getPath)).toList*/, outputDirectory)
val classLoader = AbstractFileClassLoader(outputDirectory, this.getClass.getClassLoader/*depClassLoader*/)
runObjectMethod("mypackage.Main", classLoader, "main", Seq(classOf[Array[String]]), Array.empty[String])
// Hello, World!
def compileCode(
code: String,
classpathDirectories: List[AbstractFile],
outputDirectory: AbstractFile
): Unit = {
class DriverImpl extends Driver {
private val compileCtx0 = initCtx.fresh
given Context = compileCtx0.fresh
.setSetting(
compileCtx0.settings.classpath,
classpathDirectories.map(_.path).mkString(":")
).setSetting(
compileCtx0.settings.usejavacp,
true
).setSetting(
compileCtx0.settings.outputDir,
outputDirectory
)
val compiler = newCompiler
}
val driver = new DriverImpl
import driver.given Context
val sourceFile = SourceFile(VirtualFile("(inline)", code.getBytes(StandardCharsets.UTF_8)), Codec.UTF8)
val run = driver.compiler.newRun
run.compileSources(List(sourceFile))
// val unit = run.units.head
// println("untyped tree=" + unit.untpdTree)
// println("typed tree=" + unit.tpdTree)
}
def runObjectMethod(
objectName: String,
classLoader: ClassLoader,
methodName: String,
paramClasses: Seq[Class[?]],
arguments: Any*
): Any = {
val clazz = Class.forName(s"$objectName$$", true, classLoader)
val module = clazz.getField("MODULE$").get(null)
val method = module.getClass.getMethod(methodName, paramClasses*)
method.invoke(module, arguments*)
}
Run Code Online (Sandbox Code Playgroud)
另请参阅:从 scala 3 宏中的类获取注释(在 Scala 3 中破解多阶段编程并实现我们自己的eval而不是 Scala 2context.eval或staging.run在 Scala 3 宏中禁止)。
在运行时将 scala 3 代码从字符串解析为 Scala 3 AST
| 归档时间: |
|
| 查看次数: |
1597 次 |
| 最近记录: |