[FIXED] Wie kann ich generierten Code während der Skriptlaufzeit ausführen?

Ausgabe

Während der Ausführung eines Scala-Skripts möchte ich, dass es Code generiert und diesen ausführt.

Ich dachte, ich hätte online zwei Beispiele gefunden, die funktionieren könnten, aber sie sind nicht erfolgreich

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)

  }

Dies sind vielleicht veraltete Beispiele.

Hat jemand eine funktionierende oder eine Lösung?

Lösung

Ich übernehme meine Antwort in Scala 2 unter Wie kompiliere und führe ich Scala-Code zur Laufzeit in Scala3 aus?

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
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
  • Wenn Sie eher eine scala.reflect.runtime.universe.Tree q"..."Zeichenfolge als eine einfache Zeichenfolge haben, müssen Sie sie nicht analysieren
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
  • Wenn Sie einen scala.reflect.runtime.universe.Expr reify {...}(einen statisch typisierten Wrapper über einem Baum) anstelle einer einfachen Zeichenfolge haben, müssen Sie auch nicht analysieren
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 dies dient dazu, Scala 2-Code in Scala 2 auszuführen. Wenn wir Scala 3-Code in Scala 2 ausführen möchten, können wir den standardmäßigen Scala 3 REPL- Interpreter verwenden
(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"
  • Sie können auch JSR223- Scripting verwenden. Abhängig davon, ob Sie Scala 2 oder Scala 3 (eine der beiden oben genannten Skript-Engines: Scala 2 oder Scala 3 ) in Ihrem scala-compilerKlassenpfad haben . Wenn Sie beides haben, gewinnt die zuerst hinzugefügte Abhängigkeit.scala3-compilerscala.tools.nsc.interpreter.shell.Scripteddotty.tools.repl.ScriptEngine
new javax.script.ScriptEngineManager(getClass.getClassLoader)
  .getEngineByName("scala")
  .eval("""println("Hello, World!")""")
// Hello, World!

Wenn Sie besser steuern möchten, welche Abhängigkeit verwendet wird (ohne das Projekt erneut zu importieren), können Sie Coursier verwenden und den Klassenlader angeben

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!
  • Sie können Eval in Scala 2 selbst mit dem aktuellen Compiler implementieren
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

Scala-Reflexion: Akka-Actor mit Protobuf kompilieren

Dynamische Kompilierung mehrerer Scala-Klassen zur Laufzeit

Wie wertet man Code aus, der die InterfaceStability-Annotation verwendet (der mit “illegaler zyklischer Referenz mit der Klasse InterfaceStability” fehlschlägt)?

Tensorflow in Scala-Reflexion

Scala Presentation Compiler – Minimales Beispiel

Was ist “Scala Presentation Compiler”?

„eval“ in Scala


Beantwortet von –
Dmytro Mitin


Antwort geprüft von –
Mary Flores (FixError Volunteer)

0 Shares:
Leave a Reply

Your email address will not be published. Required fields are marked *

You May Also Like