All Downloads are FREE. Search and download functionalities are using the official Maven repository.

replpp.ScriptRunner.scala Maven / Gradle / Ivy

package replpp

import dotty.tools.scripting.ScriptingDriver

import java.util.stream.Collectors
import scala.collection.immutable.{AbstractSeq, LinearSeq}
import scala.jdk.CollectionConverters.*
import scala.xml.NodeSeq

object ScriptRunner {

  def exec(config: Config): Unit = {
    val scriptFile = config.scriptFile.getOrElse(throw new AssertionError("scriptFile not defined"))
    if (!os.exists(scriptFile)) {
      throw new AssertionError(s"given script file $scriptFile does not exist")
    }

    val paramsInfoMaybe =
      if (config.params.nonEmpty) s" with params=${config.params}"
      else ""
    System.err.println(s"executing $scriptFile$paramsInfoMaybe")
    val scriptArgs: Seq[String] = {
      val commandArgs = config.command.toList
      val parameterArgs = config.params.flatMap { case (key, value) => Seq(s"--$key", value) }
      commandArgs ++ parameterArgs
    }

    // Predef code may include import statements... I didn't find a nice way to add them to the context of the
    // script file, so instead we'll just write it to the beginning of the script file.
    // That's obviously suboptimal, e.g. because it messes with the line numbers.
    // Therefor, we'll display the temp script file name to the user and not delete it, in case the script errors.
    val predefCode = allPredefCode(config)
    val predefPlusScriptFileTmp = os.temp(prefix = "scala-repl-pp-script-with-predef", suffix = ".sc")
    val scriptCode = os.read(scriptFile)
    val scriptContent = wrapForMainargs(predefCode, scriptCode)
    if (config.verbose) println(scriptContent)
    os.write.over(predefPlusScriptFileTmp, scriptContent)

    new ScriptingDriver(
      compilerArgs = compilerArgs(config, predefCode) :+ "-nowarn",
      scriptFile = predefPlusScriptFileTmp.toIO,
      scriptArgs = scriptArgs.toArray
    ).compileAndRun()
    System.err.println(s"script finished successfully")

    // if the script failed, the ScriptingDriver would have thrown an exception, in which case we
    // don't delete the temporary file which includes the predef,
    // so that the line numbers are accurate and the user can properly debug
    os.remove(predefPlusScriptFileTmp)
  }

  private def wrapForMainargs(predefCode: String, scriptCode: String): String = {
    val mainImpl =
      if (scriptCode.contains("@main")) {
        scriptCode
      } else {
        s"""@main def _execMain(): Unit = {
           |  $scriptCode
           |}
           |""".stripMargin
      }

    s"""
       |import mainargs.main // intentionally shadow any potentially given @main
       |
       |// dotty's ScriptingDriver expects an object with a `main(Array[String]): Unit`
       |object Main {
       |
       |$predefCode
       |
       |$mainImpl
       |
       |  def main(args: Array[String]): Unit = {
       |    mainargs.ParserForMethods(this).runOrExit(args.toSeq)
       |  }
       |}
       |""".stripMargin
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy