-
Notifications
You must be signed in to change notification settings - Fork 35
/
ScalaInterpreter.scala
112 lines (96 loc) · 3.81 KB
/
ScalaInterpreter.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
package org.vertx.scala.lang
import java.io.{ File, PrintWriter }
import scala.annotation.tailrec
import scala.io.Source
import scala.reflect.internal.util.BatchSourceFile
import scala.reflect.io.PlainFile
import scala.tools.nsc.Settings
import scala.tools.nsc.interpreter.{NamedParamClass, IMain}
import scala.tools.nsc.interpreter.Results.Result
import scala.tools.nsc.interpreter.Results.{ Error => InterpreterError }
import scala.tools.nsc.interpreter.Results.{ Incomplete => InterpreterIncomplete}
import scala.tools.nsc.interpreter.Results.{ Success => InterpreterSuccess }
import org.vertx.scala.core.Vertx
import scala.tools.nsc.NewLinePrintWriter
import scala.tools.nsc.ConsoleWriter
import java.net.URL
import org.vertx.scala.platform.Container
import scala.util.{Success, Failure, Try}
/**
* Scala interpreter
*
* @author Galder Zamarreño
*/
class ScalaInterpreter(
settings: Settings,
vertx: Vertx,
container: Container,
out: PrintWriter = new NewLinePrintWriter(new ConsoleWriter, true)) {
private val interpreter = new IMain(settings, out)
interpreter.setContextClassLoader()
def addBootClasspathJar(path: String): Unit =
settings.bootclasspath.append(path)
def close(): Unit = interpreter.close()
def runScript(script: URL): Try[Unit] = {
val content = Source.fromURL(script).mkString
val ops = List(
() => addImport("org.vertx.scala._"),
() => addImport("org.vertx.scala.core._"),
() => addImport("org.vertx.scala.core.buffer._"),
() => addImport("org.vertx.scala.core.dns._"),
() => addImport("org.vertx.scala.core.eventbus._"),
() => addImport("org.vertx.scala.core.file._"),
() => addImport("org.vertx.scala.core.http._"),
() => addImport("org.vertx.scala.core.json._"),
() => addImport("org.vertx.scala.core.net._"),
() => addImport("org.vertx.scala.core.streams._"),
() => bind("vertx", "org.vertx.scala.core.Vertx", vertx),
() => bind("container", "org.vertx.scala.platform.Container", container),
() => interpret(content)
)
val result = interpret(ops, InterpreterIncomplete)
result match {
case InterpreterError => Failure(new ScalaCompilerError(s"Interpreter error running $script"))
case InterpreterIncomplete => Failure(new ScalaCompilerError(s"Incomplete script $script"))
case InterpreterSuccess => Success()
}
}
private def addImport(id: String): Result =
interpret("import " + id)
private def bind(name: String, boundType: String, value: Any): Result =
verboseOrQuiet(
interpreter.bind(name, boundType, value),
interpreter.quietBind(NamedParamClass(name, boundType, value)))
private def interpret(content: String): Result =
verboseOrQuiet(
interpreter.interpret(content),
interpreter.beQuietDuring(interpreter.interpret(content)))
private def verboseOrQuiet(verbose: => Result, quiet: => Result): Result = {
if (ScalaInterpreter.isVerbose) verbose else quiet
}
def compileClass(classFile: File): Try[ClassLoader] = {
val source = new BatchSourceFile(new PlainFile(classFile))
val result = interpreter.compileSources(source)
if (result)
Success(interpreter.classLoader)
else
Failure(new IllegalArgumentException(s"Unable to compile $classFile"))
}
@tailrec
private def interpret(ops: List[() => Result], accumulate: Result): Result = {
ops match {
case List() => accumulate
case x :: xs =>
val result = x()
result match {
case InterpreterError | InterpreterIncomplete => result
case InterpreterSuccess => interpret(xs, result)
}
}
}
private class ScalaCompilerError(message: String) extends Exception(message)
}
object ScalaInterpreter {
def isVerbose: Boolean =
System.getProperty("vertx.scala.interpreter.verbose", "false").toBoolean
}