.11.do.source-code.repl.scala Maven / Gradle / Ivy
//-- license info
/*
Copyright 2010 Aaron J. Radke
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cc.drx
/* repl object to launch a repl from mvn directly from a launcher command (oh an nicely for windows too)
* The solution is essentially a combination of the scala repl programmatic doc and stack overvlow classpath trick
* https://docs.scala-lang.org/overviews/repl/embedding.html
* this excelent post is the classpath trick that finally made it work https://stackoverflow.com/a/4937135/622016
* http://www.scala-lang.org/api/2.12.3/scala-compiler/scala/tools/nsc/interpreter/ILoop.html
* https://docs.scala-lang.org/overviews/repl/embedding.html
* roll your own repl https://stackoverflow.com/a/18628517/622016
*/
object Repl{
import scala.tools.nsc.Settings
import scala.tools.nsc.interpreter._
// import scala.tools.nsc.interpreter.shell._ //TODO needed for 2.13 compiling
def main(args:Array[String]):Unit = {
//TODO install the jansi features if running in windows mode?? is this needed or already part of the sbt launcher? for now it doesn't seem to need explicit installation and seems to work in Windows since drx.Boot includes jansi itself, but non boot may not work
//--- command line args
val isVerbose = args contains "-v"
//---javax.script features
// val engine = new javax.script.ScriptEngineManager().getEngineByName("scala")
// println(engine eval code)
//--fantastic robust detection trick from https://stackoverflow.com/a/22039095/622016
val traceClasses = new Exception().getStackTrace.map{_.getClassName}
val isSBT = traceClasses contains "sbt.Run"
val isLauncher = traceClasses contains "xsbt.boot.Boot"
//-- find sbt-luancher already loaded classpaths to pass to the repl
// Uses the drx.Classpath abstrcation for cross platform classpath read and construct
val loaderClasspath = Classpath.local
//--just find the missing scala paths
//this excelent post is the classpath trick that finally made it work https://stackoverflow.com/a/4937135/622016
val scalaClasspath = {
def alreadyContains(name:String):Boolean = loaderClasspath.files.exists(_.name == name)
val bootPathPat = """/(scala-2\.[^/]+)/.*""".r //look for some directory that looks like a scala-2.12.5 like folder
val scalaPaths = loaderClasspath.files.map{_.unixPath}.find(bootPathPat existsMatch _).toList.flatMap{ path =>
for(fname <- List("jline.jar","scala-library.jar", "scala-compiler.jar") if !alreadyContains(fname)) yield
bootPathPat.replaceAllIn(path, "/$1/lib/"+fname)
}
Classpath(scalaPaths map File.apply)
}
//-- join classpaths
val classpath = if(isSBT) scalaClasspath else (scalaClasspath ++ loaderClasspath)
//--setup compiler & env settings
System.setProperty("scala.color", "true")
val settings = new Settings
settings.Yreplsync.value = true
settings.processArgumentString(
"-unchecked -deprecation -feature -Xfatal-warnings -Xfuture -Ywarn-value-discard -Yno-adapted-args -encoding UTF-8"
)
settings.classpath.value = classpath.toString
settings.usejavacp.value = if(isSBT || isLauncher) false else true //(external from sbt)
// if(isSBT) settings.embeddedDefaults[Repl.type]
// else settings.usejavacp.value = true //(external from sbt)
//---show verbose info
if(isVerbose){
println(s"#-- class trace")
traceClasses foreach println
println(s"#-- settings")
println("isSBT:"+isSBT)
println("isLauncher:"+isLauncher)
println("settings.usejavacp:"+settings.usejavacp)
println("settings:"+settings)
// println("settings.classpath:"+settings.classpath)
println("#-- injected classpath:")
println(classpath.nice)
}
//--programatic repl
// val imain = new IMain(settings)
// Console println imain.interpret("val x = 5")
// Console println imain.interpret("x+5")
//--interactive repl
val iloop = new ILoop(){
override def createInterpreter():Unit = { //example from keithohara sp5repl.scala
super.createInterpreter()
intp.quietRun("import cc.drx._") //prelude
()
}
override def prompt:String = "scala> " ansi Color.Pink //drx>
override def printWelcome:Unit = echo("Welcome to drx.cc.Repl, a scala repl with launcher support and default imports for cc.drx._")
}
iloop.process(settings)
println("Done.");
// iloop.close() //null pointer if if this is called after a close interpreter
() //end of main
}
}
object Eval{
import scala.reflect.runtime._
import scala.reflect.runtime.universe._
import scala.tools.reflect.ToolBox
// import scala.reflect.runtime.universe //val cm = universe.runtimeMirror(getClass.getClassLoader)
private val cm = universe.runtimeMirror(getClass.getClassLoader)
private val toolbox = cm.mkToolBox()
// private lazy val toolbox = scala.reflect.runtime.currentMirror.mkToolBox()
type EvalExpr = () => Any
// def compile(code: String): () => Any = toolbox compile treeOf(code)
def compile(code: String):EvalExpr = {
val tree = toolbox parse code
toolbox compile tree
}
def evalAny(code: String): Any = compile(code)()
def eval[T](code: String): T = evalAny(code).asInstanceOf[T]
def main(args:Array[String]) = {
val argsCode = args.mkString(" ").trim
val code = if(argsCode != "") argsCode else "2+2"
val res = eval[Int]( code )
println(res)
}
}