diff --git a/amm/compiler/interface/src/main/scala/ammonite/compiler/iface/CodeWrapper.scala b/amm/compiler/interface/src/main/scala/ammonite/compiler/iface/CodeWrapper.scala
new file mode 100644
index 000000000..c6ae524d3
--- /dev/null
+++ b/amm/compiler/interface/src/main/scala/ammonite/compiler/iface/CodeWrapper.scala
@@ -0,0 +1,40 @@
+package ammonite.compiler.iface
+
+import ammonite.util.{Imports, Name}
+import ammonite.util.Util.CodeSource
+
+abstract class CodeWrapper {
+ def wrapperPath: Seq[Name] = Nil
+ def apply(
+ code: String,
+ source: CodeSource,
+ imports: Imports,
+ printCode: String,
+ indexedWrapper: Name,
+ extraCode: String
+ ): (String, String, Int)
+
+ def wrapCode(codeSource: CodeSource,
+ indexedWrapperName: Name,
+ code: String,
+ printCode: String,
+ imports: Imports,
+ extraCode: String,
+ markScript: Boolean) = {
+
+ //we need to normalize topWrapper and bottomWrapper in order to ensure
+ //the snippets always use the platform-specific newLine
+ val extraCode0 =
+ if (markScript) extraCode + "/**/"
+ else extraCode
+ val (topWrapper, bottomWrapper, userCodeNestingLevel) =
+ apply(code, codeSource, imports, printCode, indexedWrapperName, extraCode0)
+ val (topWrapper0, bottomWrapper0) =
+ if (markScript) (topWrapper + "/**/ /**/" + bottomWrapper)
+ else (topWrapper, bottomWrapper)
+ val importsLen = topWrapper0.length
+
+ (topWrapper0 + code + bottomWrapper0, importsLen, userCodeNestingLevel)
+ }
+
+}
diff --git a/amm/compiler/interface/src/main/scala/ammonite/compiler/iface/Compiler.scala b/amm/compiler/interface/src/main/scala/ammonite/compiler/iface/Compiler.scala
new file mode 100644
index 000000000..d2207255d
--- /dev/null
+++ b/amm/compiler/interface/src/main/scala/ammonite/compiler/iface/Compiler.scala
@@ -0,0 +1,28 @@
+package ammonite.compiler.iface
+
+import ammonite.util.{Imports, Printer}
+
+abstract class Compiler {
+
+ def compile(
+ src: Array[Byte],
+ printer: Printer,
+ importsLen: Int,
+ userCodeNestingLevel: Int,
+ fileName: String
+ ): Option[Compiler.Output]
+
+ def preprocessor(fileName: String, markGeneratedSections: Boolean = false): Preprocessor
+
+}
+
+object Compiler {
+
+ case class Output(
+ classFiles: Vector[(String, Array[Byte])],
+ imports: Imports,
+ usedEarlierDefinitions: Option[Seq[String]]
+ )
+
+}
+
diff --git a/amm/compiler/interface/src/main/scala/ammonite/compiler/iface/CompilerBuilder.scala b/amm/compiler/interface/src/main/scala/ammonite/compiler/iface/CompilerBuilder.scala
new file mode 100644
index 000000000..eecaaeb47
--- /dev/null
+++ b/amm/compiler/interface/src/main/scala/ammonite/compiler/iface/CompilerBuilder.scala
@@ -0,0 +1,42 @@
+package ammonite.compiler.iface
+
+import java.net.URL
+import java.nio.file.Path
+
+import ammonite.util.Frame
+
+abstract class CompilerBuilder {
+
+ def newManager(
+ rtCacheDir: Option[Path],
+ headFrame: => Frame,
+ dependencyCompleter: => Option[String => (Int, Seq[String])],
+ whiteList: Set[Seq[String]],
+ initialClassLoader: ClassLoader
+ ): CompilerLifecycleManager
+
+ def create(
+ initialClassPath: Seq[URL],
+ classPath: Seq[URL],
+ dynamicClassPath: Seq[(String, Array[Byte])],
+ evalClassLoader: ClassLoader,
+ pluginClassLoader: ClassLoader,
+ reporter: Option[CompilerBuilder.Message => Unit],
+ settings: Seq[String],
+ classPathWhiteList: Set[Seq[String]],
+ lineNumberModifier: Boolean
+ ): Compiler
+
+ def scalaVersion: String
+}
+
+object CompilerBuilder {
+
+ case class Message(
+ severity: String,
+ start: Int,
+ end: Int,
+ message: String
+ )
+
+}
diff --git a/amm/compiler/interface/src/main/scala/ammonite/compiler/iface/CompilerLifecycleManager.scala b/amm/compiler/interface/src/main/scala/ammonite/compiler/iface/CompilerLifecycleManager.scala
new file mode 100644
index 000000000..382f12eea
--- /dev/null
+++ b/amm/compiler/interface/src/main/scala/ammonite/compiler/iface/CompilerLifecycleManager.scala
@@ -0,0 +1,33 @@
+package ammonite.compiler.iface
+
+import java.nio.file.Path
+
+import ammonite.util.{Frame, Printer}
+import ammonite.util.Util.ClassFiles
+
+abstract class CompilerLifecycleManager {
+ def compiler: Compiler
+ def compilationCount: Int
+
+ def preprocess(fileName: String): Preprocessor
+
+ def scalaVersion: String
+
+ def init(force: Boolean = false): Unit
+
+ def complete(
+ offset: Int,
+ previousImports: String,
+ snippet: String
+ ): (Int, Seq[String], Seq[String])
+
+ def compileClass(
+ processed: Preprocessor.Output,
+ printer: Printer,
+ fileName: String
+ ): Option[Compiler.Output]
+
+ def addToClasspath(classFiles: ClassFiles): Unit
+
+ def shutdownPressy(): Unit
+}
diff --git a/amm/compiler/interface/src/main/scala/ammonite/compiler/iface/Parser.scala b/amm/compiler/interface/src/main/scala/ammonite/compiler/iface/Parser.scala
new file mode 100644
index 000000000..1077b4098
--- /dev/null
+++ b/amm/compiler/interface/src/main/scala/ammonite/compiler/iface/Parser.scala
@@ -0,0 +1,76 @@
+package ammonite.compiler.iface
+
+import ammonite.util.ImportTree
+import ammonite.util.Util.CodeSource
+
+abstract class Parser {
+
+ def split(
+ code: String,
+ ignoreIncomplete: Boolean = true,
+ fileName: String = "(console)"
+ ): Option[Either[String, Seq[String]]]
+
+ final def parseImportHooks(
+ source: CodeSource,
+ stmts: Seq[String]
+ ): (Seq[String], Seq[ImportTree]) =
+ parseImportHooksWithIndices(source, stmts.map((0, _)))
+ def parseImportHooksWithIndices(
+ source: CodeSource,
+ stmts: Seq[(Int, String)]
+ ): (Seq[String], Seq[ImportTree])
+
+ /**
+ * Splits up a script file into its constituent blocks, each of which
+ * is a tuple of (leading-whitespace, statements). Leading whitespace
+ * is returned separately so we can later manipulate the statements e.g.
+ * by adding `val res2 = ` without the whitespace getting in the way
+ */
+ def splitScript(
+ rawCode: String,
+ fileName: String
+ ): Either[String, IndexedSeq[(String, Seq[String])]]
+
+ def scriptBlocksWithStartIndices(
+ rawCode: String,
+ fileName: String
+ ): Either[Parser.ScriptSplittingError, Seq[Parser.ScriptBlock]]
+
+ def defaultHighlight(buffer: Vector[Char],
+ comment: fansi.Attrs,
+ `type`: fansi.Attrs,
+ literal: fansi.Attrs,
+ keyword: fansi.Attrs,
+ reset: fansi.Attrs): Vector[Char]
+
+ def isObjDef(code: String): Boolean
+}
+
+object Parser {
+
+ case class ParsedImportHooks(
+ hookStatements: Seq[String],
+ importTrees: Seq[ImportTree]
+ )
+
+ case class ScriptBlock(
+ startIndex: Int,
+ ncomment: String,
+ codeWithStartIndices: Seq[(Int, String)]
+ )
+
+ object ScriptBlock {
+ def apply(
+ ncomment: String,
+ codeWithStartIndices: Seq[(Int, String)]
+ ): ScriptBlock =
+ ScriptBlock(0, ncomment, codeWithStartIndices)
+ }
+
+ class ScriptSplittingError(
+ message: String,
+ val index: Int = -1,
+ val expected: String = ""
+ ) extends Exception(message)
+}
diff --git a/amm/compiler/interface/src/main/scala/ammonite/compiler/iface/Preprocessor.scala b/amm/compiler/interface/src/main/scala/ammonite/compiler/iface/Preprocessor.scala
new file mode 100644
index 000000000..0c7843b32
--- /dev/null
+++ b/amm/compiler/interface/src/main/scala/ammonite/compiler/iface/Preprocessor.scala
@@ -0,0 +1,50 @@
+package ammonite.compiler.iface
+
+import ammonite.util.{Imports, Name, Res}
+import ammonite.util.Util.CodeSource
+
+/**
+ * Responsible for all scala-source-code-munging that happens within the
+ * Ammonite REPL.
+ *
+ * Performs several tasks:
+ *
+ * - Takes top-level Scala expressions and assigns them to `res{1, 2, 3, ...}`
+ * values so they can be accessed later in the REPL
+ *
+ * - Wraps the code snippet with an wrapper `object` since Scala doesn't allow
+ * top-level expressions
+ *
+ * - Mangles imports from our [[ammonite.util.ImportData]] data structure into a source
+ * String
+ *
+ * - Combines all of these into a complete compilation unit ready to feed into
+ * the Scala compiler
+ */
+abstract class Preprocessor {
+
+ def transform(
+ stmts: Seq[String],
+ resultIndex: String,
+ leadingSpaces: String,
+ codeSource: CodeSource,
+ indexedWrapperName: Name,
+ imports: Imports,
+ printerTemplate: String => String,
+ extraCode: String,
+ skipEmpty: Boolean,
+ markScript: Boolean,
+ codeWrapper: CodeWrapper
+ ): Res[Preprocessor.Output]
+
+}
+
+object Preprocessor {
+
+ case class Output(
+ code: String,
+ prefixCharLength: Int,
+ userCodeNestingLevel: Int
+ )
+
+}
diff --git a/amm/interp/src/main/scala-2.12.0_12/ammonite/interp/CompilerCompatibility.scala b/amm/compiler/src/main/scala-2.12.0_12/ammonite/compiler/CompilerCompatibility.scala
similarity index 96%
rename from amm/interp/src/main/scala-2.12.0_12/ammonite/interp/CompilerCompatibility.scala
rename to amm/compiler/src/main/scala-2.12.0_12/ammonite/compiler/CompilerCompatibility.scala
index d07a502b5..1a109e902 100644
--- a/amm/interp/src/main/scala-2.12.0_12/ammonite/interp/CompilerCompatibility.scala
+++ b/amm/compiler/src/main/scala-2.12.0_12/ammonite/compiler/CompilerCompatibility.scala
@@ -1,6 +1,6 @@
-package ammonite.interp
+package ammonite.compiler
-import ammonite.runtime.Classpath
+import ammonite.util.Classpath
import scala.reflect.internal.util.Position
import scala.reflect.io.FileZipArchive
diff --git a/amm/interp/src/main/scala-2.12.0_8/ammonite/interp/ExtraCompilerCompatibility.scala b/amm/compiler/src/main/scala-2.12.0_8/ammonite/compiler/ExtraCompilerCompatibility.scala
similarity index 92%
rename from amm/interp/src/main/scala-2.12.0_8/ammonite/interp/ExtraCompilerCompatibility.scala
rename to amm/compiler/src/main/scala-2.12.0_8/ammonite/compiler/ExtraCompilerCompatibility.scala
index ff2a3772e..6d58f3122 100644
--- a/amm/interp/src/main/scala-2.12.0_8/ammonite/interp/ExtraCompilerCompatibility.scala
+++ b/amm/compiler/src/main/scala-2.12.0_8/ammonite/compiler/ExtraCompilerCompatibility.scala
@@ -1,4 +1,4 @@
-package ammonite.interp
+package ammonite.compiler
import scala.reflect.io.FileZipArchive
import scala.tools.nsc.Settings
diff --git a/amm/interp/src/main/scala-2.12.10-2.13.1+/scala/tools/nsc/AmmClassPath.scala b/amm/compiler/src/main/scala-2.12.10-2.13.1+/scala/tools/nsc/AmmClassPath.scala
similarity index 95%
rename from amm/interp/src/main/scala-2.12.10-2.13.1+/scala/tools/nsc/AmmClassPath.scala
rename to amm/compiler/src/main/scala-2.12.10-2.13.1+/scala/tools/nsc/AmmClassPath.scala
index 65bb52110..3efa67977 100644
--- a/amm/interp/src/main/scala-2.12.10-2.13.1+/scala/tools/nsc/AmmClassPath.scala
+++ b/amm/compiler/src/main/scala-2.12.10-2.13.1+/scala/tools/nsc/AmmClassPath.scala
@@ -3,7 +3,7 @@ package scala.tools.nsc
import java.io.File
import java.net.URL
-import ammonite.interp.internal.CustomURLZipArchive
+import ammonite.compiler.internal.CustomURLZipArchive
import scala.reflect.io.AbstractFile
import scala.tools.nsc.classpath.FileUtils.AbstractFileOps
diff --git a/amm/interp/src/main/scala-2.12.10-2.13.1+/scala/tools/nsc/WhiteListClasspath.scala b/amm/compiler/src/main/scala-2.12.10-2.13.1+/scala/tools/nsc/WhiteListClasspath.scala
similarity index 100%
rename from amm/interp/src/main/scala-2.12.10-2.13.1+/scala/tools/nsc/WhiteListClasspath.scala
rename to amm/compiler/src/main/scala-2.12.10-2.13.1+/scala/tools/nsc/WhiteListClasspath.scala
diff --git a/amm/interp/src/main/scala-2.12.13+/ammonite/interp/CompilerCompatibility.scala b/amm/compiler/src/main/scala-2.12.13+/ammonite/compiler/CompilerCompatibility.scala
similarity index 96%
rename from amm/interp/src/main/scala-2.12.13+/ammonite/interp/CompilerCompatibility.scala
rename to amm/compiler/src/main/scala-2.12.13+/ammonite/compiler/CompilerCompatibility.scala
index 85f7458e5..c36762350 100644
--- a/amm/interp/src/main/scala-2.12.13+/ammonite/interp/CompilerCompatibility.scala
+++ b/amm/compiler/src/main/scala-2.12.13+/ammonite/compiler/CompilerCompatibility.scala
@@ -1,6 +1,6 @@
-package ammonite.interp
+package ammonite.compiler
-import ammonite.runtime.Classpath
+import ammonite.util.Classpath
import scala.reflect.internal.util.Position
import scala.reflect.io.FileZipArchive
diff --git a/amm/interp/src/main/scala-2.12.13+/ammonite/interp/MakeReporter.scala b/amm/compiler/src/main/scala-2.12.13+/ammonite/compiler/MakeReporter.scala
similarity index 95%
rename from amm/interp/src/main/scala-2.12.13+/ammonite/interp/MakeReporter.scala
rename to amm/compiler/src/main/scala-2.12.13+/ammonite/compiler/MakeReporter.scala
index 42580afe5..3b9fbf5f2 100644
--- a/amm/interp/src/main/scala-2.12.13+/ammonite/interp/MakeReporter.scala
+++ b/amm/compiler/src/main/scala-2.12.13+/ammonite/compiler/MakeReporter.scala
@@ -1,6 +1,6 @@
-package ammonite.interp
+package ammonite.compiler
-import ammonite.runtime.Classpath
+import ammonite.util.Classpath
import scala.reflect.internal.util.Position
import scala.reflect.io.FileZipArchive
diff --git a/amm/interp/src/main/scala-2.12.9+/ammonite/interp/ExtraCompilerCompatibility.scala b/amm/compiler/src/main/scala-2.12.9+/ammonite/compiler/ExtraCompilerCompatibility.scala
similarity index 93%
rename from amm/interp/src/main/scala-2.12.9+/ammonite/interp/ExtraCompilerCompatibility.scala
rename to amm/compiler/src/main/scala-2.12.9+/ammonite/compiler/ExtraCompilerCompatibility.scala
index 9ba9ff394..1f79dbffe 100644
--- a/amm/interp/src/main/scala-2.12.9+/ammonite/interp/ExtraCompilerCompatibility.scala
+++ b/amm/compiler/src/main/scala-2.12.9+/ammonite/compiler/ExtraCompilerCompatibility.scala
@@ -1,4 +1,4 @@
-package ammonite.interp
+package ammonite.compiler
import scala.reflect.io.FileZipArchive
import scala.tools.nsc.Settings
diff --git a/amm/interp/src/main/scala-2.13.1+/ammonite/interp/MakeReporter.scala b/amm/compiler/src/main/scala-2.13.1+/ammonite/compiler/MakeReporter.scala
similarity index 95%
rename from amm/interp/src/main/scala-2.13.1+/ammonite/interp/MakeReporter.scala
rename to amm/compiler/src/main/scala-2.13.1+/ammonite/compiler/MakeReporter.scala
index 42580afe5..3b9fbf5f2 100644
--- a/amm/interp/src/main/scala-2.13.1+/ammonite/interp/MakeReporter.scala
+++ b/amm/compiler/src/main/scala-2.13.1+/ammonite/compiler/MakeReporter.scala
@@ -1,6 +1,6 @@
-package ammonite.interp
+package ammonite.compiler
-import ammonite.runtime.Classpath
+import ammonite.util.Classpath
import scala.reflect.internal.util.Position
import scala.reflect.io.FileZipArchive
diff --git a/amm/interp/src/main/scala-2.13/ammonite/interp/CompilerCompatibility.scala b/amm/compiler/src/main/scala-2.13/ammonite/compiler/CompilerCompatibility.scala
similarity index 97%
rename from amm/interp/src/main/scala-2.13/ammonite/interp/CompilerCompatibility.scala
rename to amm/compiler/src/main/scala-2.13/ammonite/compiler/CompilerCompatibility.scala
index 46bfd1d87..0e36feaf5 100644
--- a/amm/interp/src/main/scala-2.13/ammonite/interp/CompilerCompatibility.scala
+++ b/amm/compiler/src/main/scala-2.13/ammonite/compiler/CompilerCompatibility.scala
@@ -1,6 +1,6 @@
-package ammonite.interp
+package ammonite.compiler
-import ammonite.runtime.Classpath
+import ammonite.util.Classpath
import scala.reflect.internal.util.Position
import scala.reflect.io.FileZipArchive
diff --git a/amm/interp/src/main/scala-not-2.12.10-2.13.1+/scala/tools/nsc/AmmClassPath.scala b/amm/compiler/src/main/scala-not-2.12.10-2.13.1+/scala/tools/nsc/AmmClassPath.scala
similarity index 94%
rename from amm/interp/src/main/scala-not-2.12.10-2.13.1+/scala/tools/nsc/AmmClassPath.scala
rename to amm/compiler/src/main/scala-not-2.12.10-2.13.1+/scala/tools/nsc/AmmClassPath.scala
index 52e38fc6a..d79fbe978 100644
--- a/amm/interp/src/main/scala-not-2.12.10-2.13.1+/scala/tools/nsc/AmmClassPath.scala
+++ b/amm/compiler/src/main/scala-not-2.12.10-2.13.1+/scala/tools/nsc/AmmClassPath.scala
@@ -3,7 +3,7 @@ package scala.tools.nsc
import java.io.File
import java.net.URL
-import ammonite.interp.internal.CustomURLZipArchive
+import ammonite.compiler.internal.CustomURLZipArchive
import scala.reflect.io.AbstractFile
import scala.tools.nsc.classpath.FileUtils.AbstractFileOps
diff --git a/amm/interp/src/main/scala-not-2.12.10-2.13.1+/scala/tools/nsc/WhiteListClasspath.scala b/amm/compiler/src/main/scala-not-2.12.10-2.13.1+/scala/tools/nsc/WhiteListClasspath.scala
similarity index 100%
rename from amm/interp/src/main/scala-not-2.12.10-2.13.1+/scala/tools/nsc/WhiteListClasspath.scala
rename to amm/compiler/src/main/scala-not-2.12.10-2.13.1+/scala/tools/nsc/WhiteListClasspath.scala
diff --git a/amm/interp/src/main/scala-not-2.12.13+-2.13.1+/ammonite/interp/MakeReporter.scala b/amm/compiler/src/main/scala-not-2.12.13+-2.13.1+/ammonite/compiler/MakeReporter.scala
similarity index 95%
rename from amm/interp/src/main/scala-not-2.12.13+-2.13.1+/ammonite/interp/MakeReporter.scala
rename to amm/compiler/src/main/scala-not-2.12.13+-2.13.1+/ammonite/compiler/MakeReporter.scala
index 466618ab1..f9775817b 100644
--- a/amm/interp/src/main/scala-not-2.12.13+-2.13.1+/ammonite/interp/MakeReporter.scala
+++ b/amm/compiler/src/main/scala-not-2.12.13+-2.13.1+/ammonite/compiler/MakeReporter.scala
@@ -1,6 +1,6 @@
-package ammonite.interp
+package ammonite.compiler
-import ammonite.runtime.Classpath
+import ammonite.util.Classpath
import scala.reflect.internal.util.Position
import scala.reflect.io.FileZipArchive
diff --git a/amm/interp/src/main/scala/ammonite/interp/AmmonitePlugin.scala b/amm/compiler/src/main/scala/ammonite/compiler/AmmonitePlugin.scala
similarity index 99%
rename from amm/interp/src/main/scala/ammonite/interp/AmmonitePlugin.scala
rename to amm/compiler/src/main/scala/ammonite/compiler/AmmonitePlugin.scala
index c3596f18e..5fb543b71 100644
--- a/amm/interp/src/main/scala/ammonite/interp/AmmonitePlugin.scala
+++ b/amm/compiler/src/main/scala/ammonite/compiler/AmmonitePlugin.scala
@@ -1,4 +1,4 @@
-package ammonite.interp
+package ammonite.compiler
import ammonite.util.{ImportData, Name, Util}
diff --git a/amm/interp/src/main/scala/ammonite/interp/CodeClassWrapper.scala b/amm/compiler/src/main/scala/ammonite/compiler/CodeClassWrapper.scala
similarity index 98%
rename from amm/interp/src/main/scala/ammonite/interp/CodeClassWrapper.scala
rename to amm/compiler/src/main/scala/ammonite/compiler/CodeClassWrapper.scala
index 10c0228dc..9ae7bfe91 100644
--- a/amm/interp/src/main/scala/ammonite/interp/CodeClassWrapper.scala
+++ b/amm/compiler/src/main/scala/ammonite/compiler/CodeClassWrapper.scala
@@ -1,5 +1,6 @@
-package ammonite.interp
+package ammonite.compiler
+import ammonite.compiler.iface.CodeWrapper
import ammonite.util._
import ammonite.util.Util.{CodeSource, newLine, normalizeNewlines}
diff --git a/amm/interp/src/main/scala/ammonite/interp/Compiler.scala b/amm/compiler/src/main/scala/ammonite/compiler/Compiler.scala
similarity index 85%
rename from amm/interp/src/main/scala/ammonite/interp/Compiler.scala
rename to amm/compiler/src/main/scala/ammonite/compiler/Compiler.scala
index 01bd2f229..efa716441 100644
--- a/amm/interp/src/main/scala/ammonite/interp/Compiler.scala
+++ b/amm/compiler/src/main/scala/ammonite/compiler/Compiler.scala
@@ -1,11 +1,10 @@
-package ammonite.interp
+package ammonite.compiler
import java.io.OutputStream
-import java.nio.charset.StandardCharsets
-import ammonite.runtime.{Classpath, Evaluator}
-import ammonite.util.{ImportData, Imports, Name, Printer}
+import ammonite.compiler.iface.{Compiler => ICompiler, Preprocessor}
+import ammonite.util.{Classpath, ImportData, Imports, Printer}
import ammonite.util.Util.newLine
import scala.collection.mutable
@@ -22,7 +21,6 @@ import scala.tools.nsc.classpath.{
import scala.tools.nsc.{CustomZipAndJarFileLookupFactory, Global, Settings}
import scala.tools.nsc.interactive.Response
import scala.tools.nsc.plugins.Plugin
-import scala.util.Try
/**
@@ -36,19 +34,8 @@ import scala.util.Try
* classfile per source-string (e.g. inner classes, or lambdas). Also lets
* you query source strings using an in-built presentation compiler
*/
-trait Compiler{
+trait Compiler extends ICompiler {
def compiler: nsc.Global
- def compile(src: Array[Byte],
- printer: Printer,
- importsLen0: Int,
- userCodeNestingLevel: Int,
- fileName: String): Option[Compiler.Output]
-
- def search(name: scala.reflect.runtime.universe.Type): Option[String]
- /**
- * Either the statements that were parsed or the error message
- */
- def parse(fileName: String, line: String): Either[String, Seq[Global#Tree]]
var importsLen = 0
var userCodeNestingLevel = -1
@@ -81,15 +68,6 @@ object Compiler{
// should catch this and return before getting here
case Nil => ???
}
- /**
- * If the Option is None, it means compilation failed
- * Otherwise it's a Traversable of (filename, bytes) tuples
- */
- case class Output(
- classFiles: Vector[(String, Array[Byte])],
- imports: Imports,
- usedEarlierDefinitions: Option[Seq[String]]
- )
/**
* Converts a bunch of bytes into Scalac's weird VirtualFile class
@@ -141,6 +119,9 @@ object Compiler{
initialClassPath: Seq[java.net.URL],
lineNumberModifier: Boolean = true): Compiler = new Compiler{
+ def preprocessor(fileName: String, markGeneratedSections: Boolean): Preprocessor =
+ new DefaultPreprocessor(parse(fileName, _), markGeneratedSections)
+
if(sys.env.contains("DIE"))???
val PluginXML = "scalac-plugin.xml"
lazy val plugins0 = {
@@ -236,7 +217,7 @@ object Compiler{
evalClassloader,
createPlugins = g => {
List(
- new ammonite.interp.AmmonitePlugin(
+ new ammonite.compiler.AmmonitePlugin(
g,
lastImports = _,
uses => usedEarlierDefinitions = Some(uses),
@@ -277,40 +258,6 @@ object Compiler{
(vd, reporter, scalac)
}
- def search(target: scala.reflect.runtime.universe.Type) = {
- def resolve(path: String*): compiler.Symbol = {
- var curr = path.toList
- var start: compiler.Symbol = compiler.RootClass
- while(curr != Nil){
- val head :: rest = curr
- start = start.typeSignature.member(compiler.newTermName(head))
- curr = rest
- }
- start
- }
- var thingsInScope = Map[compiler.Symbol, List[compiler.Name]](
- resolve() -> List(),
- resolve("java", "lang") -> List(),
- resolve("scala") -> List(),
- resolve("scala", "Predef") -> List()
- )
- var level = 5
- var found: Option[String] = None
- while(level > 0){
- thingsInScope = for {
- (sym, path) <- thingsInScope
- // No clue why this one blows up
- m <- Try(sym.typeSignature.members).toOption.toSeq.flatten
- } yield (m, m.name :: path)
- thingsInScope.find(target.typeSymbol.fullName == _._1.fullName).foreach{ path =>
- level = 0
- found = Some(path._2.mkString("."))
- }
- }
- found
- }
-
-
/**
* Compiles a blob of bytes and spits of a list of classfiles
@@ -322,7 +269,7 @@ object Compiler{
printer: Printer,
importsLen0: Int,
userCodeNestingLevel: Int,
- fileName: String): Option[Output] = {
+ fileName: String): Option[ICompiler.Output] = {
def enumerateVdFiles(d: VirtualDirectory): Iterator[AbstractFile] = {
val (subs, files) = d.iterator.partition(_.isDirectory)
@@ -359,7 +306,7 @@ object Compiler{
}
val imports = lastImports.toList
- Some(Output(files, Imports(imports), usedEarlierDefinitions))
+ Some(ICompiler.Output(files, Imports(imports), usedEarlierDefinitions))
}
}
diff --git a/amm/compiler/src/main/scala/ammonite/compiler/CompilerBuilder.scala b/amm/compiler/src/main/scala/ammonite/compiler/CompilerBuilder.scala
new file mode 100644
index 000000000..5958b2d4f
--- /dev/null
+++ b/amm/compiler/src/main/scala/ammonite/compiler/CompilerBuilder.scala
@@ -0,0 +1,91 @@
+package ammonite.compiler
+
+import java.net.URL
+import java.nio.file.Path
+
+import ammonite.compiler.iface.{
+ Compiler => ICompiler,
+ CompilerBuilder => ICompilerBuilder
+}
+import ammonite.util.Frame
+
+import scala.collection.mutable
+import scala.reflect.internal.util.{NoPosition, Position}
+import scala.reflect.io.VirtualDirectory
+import scala.tools.nsc.Settings
+
+
+object CompilerBuilder extends ICompilerBuilder {
+ def create(
+ initialClassPath: Seq[URL],
+ classPath: Seq[URL],
+ dynamicClassPath: Seq[(String, Array[Byte])],
+ evalClassLoader: ClassLoader,
+ pluginClassLoader: ClassLoader,
+ reporter: Option[ICompilerBuilder.Message => Unit],
+ settings: Seq[String],
+ classPathWhiteList: Set[Seq[String]],
+ lineNumberModifier: Boolean
+ ): ICompiler = {
+
+ val vd = new VirtualDirectory("(memory)", None)
+ Compiler.addToClasspath(dynamicClassPath, vd)
+
+ val scalacSettings = {
+ // not 100% sure error collection is correct (duplicates?)
+ val errors = new mutable.ListBuffer[String]
+ val settings0 = new Settings(err => errors += err)
+ val (_, unparsed) = settings0.processArguments(settings.toList, processAll = true)
+ for (arg <- unparsed)
+ errors += s"Unrecognized argument: $arg"
+ // TODO Report the errors via reporter?
+ settings0
+ }
+
+ val scalacReporterOpt = reporter.map { f =>
+ def report(pos: Position, message: String, severity: String) = {
+ val (start, end) =
+ if (pos == NoPosition) (0, 0)
+ else (pos.start, pos.end)
+ val msg = ICompilerBuilder.Message(severity, start, end, message)
+ f(msg)
+ }
+ MakeReporter.makeReporter(
+ (pos, msg) => report(pos, msg, "ERROR"),
+ (pos, msg) => report(pos, msg, "WARNING"),
+ (pos, msg) => report(pos, msg, "INFO"),
+ scalacSettings
+ )
+ }
+
+ Compiler(
+ classPath,
+ vd,
+ evalClassLoader,
+ pluginClassLoader,
+ () => (),
+ scalacReporterOpt,
+ scalacSettings,
+ classPathWhiteList.map(_.toSeq).toSet,
+ initialClassPath,
+ lineNumberModifier
+ )
+ }
+
+ def newManager(
+ rtCacheDir: Option[Path],
+ headFrame: => Frame,
+ dependencyCompleter: => Option[String => (Int, Seq[String])],
+ whiteList: Set[Seq[String]],
+ initialClassLoader: ClassLoader
+ ): CompilerLifecycleManager =
+ new CompilerLifecycleManager(
+ rtCacheDir,
+ headFrame,
+ dependencyCompleter,
+ whiteList,
+ initialClassLoader
+ )
+
+ def scalaVersion = scala.util.Properties.versionNumberString
+}
diff --git a/amm/compiler/src/main/scala/ammonite/compiler/CompilerExtensions.scala b/amm/compiler/src/main/scala/ammonite/compiler/CompilerExtensions.scala
new file mode 100644
index 000000000..0bc045ed2
--- /dev/null
+++ b/amm/compiler/src/main/scala/ammonite/compiler/CompilerExtensions.scala
@@ -0,0 +1,46 @@
+package ammonite.compiler
+
+import ammonite.interp.api.InterpAPI
+import ammonite.repl.api.ReplAPI
+
+object CompilerExtensions {
+
+ implicit class CompilerInterpAPIExtensions(private val api: InterpAPI) extends AnyVal {
+
+ private def compilerManager = api._compilerManager.asInstanceOf[CompilerLifecycleManager]
+
+ /**
+ * Configures the current compiler, or if the compiler hasn't been initialized
+ * yet, registers the configuration callback and applies it to the compiler
+ * when it ends up being initialized later
+ */
+ def configureCompiler(c: scala.tools.nsc.Global => Unit): Unit =
+ compilerManager.configureCompiler(c)
+
+ /**
+ * Pre-configures the next compiler. Useful for tuning options that are
+ * used during parsing such as -Yrangepos
+ */
+ def preConfigureCompiler(c: scala.tools.nsc.Settings => Unit): Unit =
+ compilerManager.preConfigureCompiler(c)
+ }
+
+ implicit class CompilerReplAPIExtensions(private val api: ReplAPI) extends AnyVal {
+
+ private def compilerManager = api._compilerManager.asInstanceOf[CompilerLifecycleManager]
+
+
+ /**
+ * Access the compiler to do crazy things if you really want to!
+ */
+ def compiler: scala.tools.nsc.Global =
+ compilerManager.compiler.compiler
+
+ /**
+ * Access the presentation compiler to do even crazier things if you really want to!
+ */
+ def interactiveCompiler: scala.tools.nsc.interactive.Global =
+ compilerManager.pressy.compiler
+ }
+
+}
diff --git a/amm/interp/src/main/scala/ammonite/interp/CompilerLifecycleManager.scala b/amm/compiler/src/main/scala/ammonite/compiler/CompilerLifecycleManager.scala
similarity index 84%
rename from amm/interp/src/main/scala/ammonite/interp/CompilerLifecycleManager.scala
rename to amm/compiler/src/main/scala/ammonite/compiler/CompilerLifecycleManager.scala
index 7796b2b1e..9c297a539 100644
--- a/amm/interp/src/main/scala/ammonite/interp/CompilerLifecycleManager.scala
+++ b/amm/compiler/src/main/scala/ammonite/compiler/CompilerLifecycleManager.scala
@@ -1,8 +1,14 @@
-package ammonite.interp
+package ammonite.compiler
-import ammonite.runtime._
+import ammonite.compiler.iface.{
+ Compiler => ICompiler,
+ CompilerLifecycleManager => ICompilerLifecycleManager,
+ Preprocessor
+}
import ammonite.util.Util._
-import ammonite.util._
+import ammonite.util.{Classpath, Frame, Printer}
+
+import java.nio.file.Path
import scala.collection.mutable
import scala.reflect.io.VirtualDirectory
@@ -21,13 +27,14 @@ import scala.tools.nsc.Settings
* than necessary
*/
class CompilerLifecycleManager(
- storage: Storage,
- headFrame: => Frame,
+ rtCacheDir: Option[Path],
+ headFrame: => ammonite.util.Frame,
dependencyCompleteOpt: => Option[String => (Int, Seq[String])],
classPathWhitelist: Set[Seq[String]],
initialClassLoader: ClassLoader
-){
+) extends ICompilerLifecycleManager {
+ def scalaVersion = scala.util.Properties.versionNumberString
private[this] object Internal{
@@ -55,7 +62,7 @@ class CompilerLifecycleManager(
def preprocess(fileName: String) = synchronized{
init()
- DefaultPreprocessor(compiler.parse(fileName, _))
+ compiler.preprocessor(fileName)
}
@@ -82,9 +89,9 @@ class CompilerLifecycleManager(
val settings = Option(compiler).fold(new Settings)(_.compiler.settings.copy)
onSettingsInit.foreach(_(settings))
- val initialClassPath = Classpath.classpath(initialClassLoader, storage)
+ val initialClassPath = Classpath.classpath(initialClassLoader, rtCacheDir)
val headFrameClassPath =
- Classpath.classpath(headFrame.classloader, storage)
+ Classpath.classpath(headFrame.classloader, rtCacheDir)
Internal.compiler = Compiler(
headFrameClassPath,
@@ -103,13 +110,13 @@ class CompilerLifecycleManager(
// Pressy is lazy, so the actual presentation compiler won't get instantiated
// & initialized until one of the methods on it is actually used
Internal.pressy = Pressy(
- Classpath.classpath(headFrame.classloader, storage),
+ headFrameClassPath,
dynamicClasspath,
headFrame.classloader,
settings.copy(),
dependencyCompleteOpt,
classPathWhitelist,
- Classpath.classpath(initialClassLoader, storage)
+ initialClassPath
)
Internal.preConfiguredSettingsChanged = false
@@ -121,14 +128,9 @@ class CompilerLifecycleManager(
pressy.complete(offset, previousImports, snippet)
}
- def search(target: scala.reflect.runtime.universe.Type) = synchronized{
- init()
- compiler.search(target)
- }
-
def compileClass(processed: Preprocessor.Output,
printer: Printer,
- fileName: String): Res[Compiler.Output] = synchronized{
+ fileName: String): Option[ICompiler.Output] = synchronized{
// Enforce the invariant that every piece of code Ammonite ever compiles,
// gets run within the `ammonite` package. It's further namespaced into
// things like `ammonite.$file` or `ammonite.$sess`, but it has to be
@@ -136,20 +138,15 @@ class CompilerLifecycleManager(
assert(processed.code.trim.startsWith("package ammonite"))
init()
- for {
- compiled <- Res.Success{
- compiler.compile(
- processed.code.getBytes(scala.util.Properties.sourceEncoding),
- printer,
- processed.prefixCharLength,
- processed.userCodeNestingLevel,
- fileName
- )
- }
- _ = Internal.compilationCount += 1
- output <- Res(compiled, "Compilation Failed")
- } yield output
-
+ val compiled = compiler.compile(
+ processed.code.getBytes(scala.util.Properties.sourceEncoding),
+ printer,
+ processed.prefixCharLength,
+ processed.userCodeNestingLevel,
+ fileName
+ )
+ Internal.compilationCount += 1
+ compiled
}
def configureCompiler(callback: scala.tools.nsc.Global => Unit) = synchronized{
diff --git a/amm/interp/src/main/scala/ammonite/interp/CodeWrapper.scala b/amm/compiler/src/main/scala/ammonite/compiler/DefaultCodeWrapper.scala
similarity index 69%
rename from amm/interp/src/main/scala/ammonite/interp/CodeWrapper.scala
rename to amm/compiler/src/main/scala/ammonite/compiler/DefaultCodeWrapper.scala
index 216debe00..06d5c62f9 100644
--- a/amm/interp/src/main/scala/ammonite/interp/CodeWrapper.scala
+++ b/amm/compiler/src/main/scala/ammonite/compiler/DefaultCodeWrapper.scala
@@ -1,20 +1,11 @@
-package ammonite.interp
+package ammonite.compiler
+import ammonite.compiler.iface.CodeWrapper
import ammonite.util._
import ammonite.util.Util.{CodeSource, normalizeNewlines}
-trait CodeWrapper{
- def wrapperPath: Seq[Name] = Nil
- def apply(
- code: String,
- source: CodeSource,
- imports: Imports,
- printCode: String,
- indexedWrapperName: Name,
- extraCode: String
- ): (String, String, Int)
-}
-object CodeWrapper extends CodeWrapper{
+
+object DefaultCodeWrapper extends CodeWrapper{
private val userCodeNestingLevel = 1
def apply(
code: String,
diff --git a/amm/interp/src/main/scala/ammonite/interp/DefaultPreprocessor.scala b/amm/compiler/src/main/scala/ammonite/compiler/DefaultPreprocessor.scala
similarity index 96%
rename from amm/interp/src/main/scala/ammonite/interp/DefaultPreprocessor.scala
rename to amm/compiler/src/main/scala/ammonite/compiler/DefaultPreprocessor.scala
index 3b414720b..ff5f926be 100644
--- a/amm/interp/src/main/scala/ammonite/interp/DefaultPreprocessor.scala
+++ b/amm/compiler/src/main/scala/ammonite/compiler/DefaultPreprocessor.scala
@@ -1,5 +1,6 @@
-package ammonite.interp
+package ammonite.compiler
+import ammonite.compiler.iface.{CodeWrapper, Preprocessor}
import ammonite.util._
import ammonite.util.Util.{CodeSource, newLine}
@@ -14,9 +15,6 @@ object DefaultPreprocessor {
case m: G#MemberDef => m.mods.isPrivate
case _ => false
}
-
- def apply(parse: => String => Either[String, Seq[G#Tree]]): Preprocessor =
- new DefaultPreprocessor(parse)
}
class DefaultPreprocessor(parse: => String => Either[String, Seq[G#Tree]],
@@ -40,10 +38,10 @@ class DefaultPreprocessor(parse: => String => Either[String, Seq[G#Tree]],
assert(codeSource.pkgName.head == Name("ammonite"))
for{
Expanded(code, printer) <- expandStatements(stmts, resultIndex, skipEmpty)
- (wrappedCode, importsLength, userCodeNestingLevel) = Preprocessor.wrapCode(
+ (wrappedCode, importsLength, userCodeNestingLevel) = codeWrapper.wrapCode(
codeSource, indexedWrapperName, leadingSpaces + code,
printerTemplate(printer.mkString(", ")),
- imports, extraCode, markScript, codeWrapper
+ imports, extraCode, markScript
)
} yield Preprocessor.Output(wrappedCode, importsLength, userCodeNestingLevel)
}
diff --git a/amm/repl/src/main/scala/ammonite/repl/Highlighter.scala b/amm/compiler/src/main/scala/ammonite/compiler/Highlighter.scala
similarity index 83%
rename from amm/repl/src/main/scala/ammonite/repl/Highlighter.scala
rename to amm/compiler/src/main/scala/ammonite/compiler/Highlighter.scala
index f4f2bb33c..5c434ad52 100644
--- a/amm/repl/src/main/scala/ammonite/repl/Highlighter.scala
+++ b/amm/compiler/src/main/scala/ammonite/compiler/Highlighter.scala
@@ -1,8 +1,6 @@
-package ammonite.repl
+package ammonite.compiler
-import ammonite.interp.Parsers
-
import fastparse._, NoWhitespace._
import scalaparse.Scala._
@@ -28,14 +26,6 @@ object Highlighter {
}.reduce(_ ++ _).render.toVector
}
- def defaultHighlight(buffer: Vector[Char],
- comment: fansi.Attrs,
- `type`: fansi.Attrs,
- literal: fansi.Attrs,
- keyword: fansi.Attrs,
- reset: fansi.Attrs) = {
- defaultHighlight0(Parsers.Splitter(_), buffer, comment, `type`, literal, keyword, reset)
- }
def defaultHighlight0(parser: P[_] => P[Any],
buffer: Vector[Char],
@@ -49,14 +39,6 @@ object Highlighter {
flattenIndices(boundedIndices, buffer)
}
- def defaultHighlightIndices(buffer: Vector[Char],
- comment: fansi.Attrs,
- `type`: fansi.Attrs,
- literal: fansi.Attrs,
- keyword: fansi.Attrs,
- reset: fansi.Attrs) = Highlighter.defaultHighlightIndices0(
- Parsers.Splitter(_), buffer, comment, `type`, literal, keyword, reset
- )
def defaultHighlightIndices0(parser: P[_] => P[Any],
buffer: Vector[Char],
comment: fansi.Attrs,
diff --git a/amm/compiler/src/main/scala/ammonite/compiler/Parsers.scala b/amm/compiler/src/main/scala/ammonite/compiler/Parsers.scala
new file mode 100644
index 000000000..83fe5a4cc
--- /dev/null
+++ b/amm/compiler/src/main/scala/ammonite/compiler/Parsers.scala
@@ -0,0 +1,277 @@
+package ammonite.compiler
+
+import ammonite.compiler.iface.{Parser => IParser, _}
+import ammonite.util.ImportTree
+import ammonite.util.Util.{CodeSource, newLine, windowsPlatform}
+
+import scala.collection.mutable
+
+object Parsers extends IParser {
+
+ import fastparse._
+
+ import ScalaWhitespace._
+ import scalaparse.Scala._
+
+ // For some reason Scala doesn't import this by default
+ private def `_`[_: P] = scalaparse.Scala.`_`
+
+
+ private def ImportSplitter[_: P]: P[Seq[ammonite.util.ImportTree]] = {
+ def IdParser = P( (Id | `_` ).! ).map(
+ s => if (s(0) == '`') s.drop(1).dropRight(1) else s
+ )
+ def Selector = P( IdParser ~ (`=>` ~/ IdParser).? )
+ def Selectors = P( "{" ~/ Selector.rep(sep = ","./) ~ "}" )
+ def BulkImport = P( `_`).map(
+ _ => Seq("_" -> None)
+ )
+ def Prefix = P( IdParser.rep(1, sep = ".") )
+ def Suffix = P( "." ~/ (BulkImport | Selectors) )
+ def ImportExpr: P[ammonite.util.ImportTree] = {
+ // Manually use `WL0` parser here, instead of relying on WhitespaceApi, as
+ // we do not want the whitespace to be consumed even if the WL0 parser parses
+ // to the end of the input (which is the default behavior for WhitespaceApi)
+ P( Index ~~ Prefix ~~ (WL0 ~~ Suffix).? ~~ Index).map{
+ case (start, idSeq, selectors, end) =>
+ ammonite.util.ImportTree(idSeq, selectors, start, end)
+ }
+ }
+ P( `import` ~/ ImportExpr.rep(1, sep = ","./) )
+ }
+
+ private def PatVarSplitter[_: P] = {
+ def Prefixes = P(Prelude ~ (`var` | `val`))
+ def Lhs = P( Prefixes ~/ BindPattern.rep(1, "," ~/ Pass) ~ (`:` ~/ Type).? )
+ P( Lhs.! ~ (`=` ~/ WL ~ StatCtx.Expr.!) ~ End )
+ }
+ private def patVarSplit(code: String) = {
+ val Parsed.Success((lhs, rhs), _) = parse(code, PatVarSplitter(_))
+ (lhs, rhs)
+ }
+
+ private def Prelude[_: P] = P( (Annot ~ OneNLMax).rep ~ (Mod ~/ Pass).rep )
+
+ private def TmplStat[_: P] = P( Import | Prelude ~ BlockDef | StatCtx.Expr )
+
+
+ // Do this funny ~~WS thing to make sure we capture the whitespace
+ // together with each statement; otherwise, by default, it gets discarded.
+ //
+ // After each statement, there must either be `Semis`, a "}" marking the
+ // end of the block, or the `End` of the input
+ private def StatementBlock[_: P](blockSep: => P0) =
+ P( Semis.? ~ (Index ~ (!blockSep ~ TmplStat ~~ WS ~~ (Semis | &("}") | End)).!).repX)
+
+ private def Splitter0[_: P] = P( StatementBlock(Fail) )
+ def Splitter[_: P] = P( ("{" ~ Splitter0 ~ "}" | Splitter0) ~ End )
+
+ private def ObjParser[_: P] = P( ObjDef )
+
+ /**
+ * Attempts to break a code blob into multiple statements. Returns `None` if
+ * it thinks the code blob is "incomplete" and requires more input
+ */
+ def split(
+ code: String,
+ ignoreIncomplete: Boolean,
+ fileName: String
+ ): Option[Either[String, Seq[String]]] =
+ if (ignoreIncomplete) {
+ // We use `instrument` to detect when the parser has reached the end of the
+ // input, any time during the parse. If it has done so, and failed, we
+ // consider the input incomplete.
+ var furthest = 0
+ val instrument = new fastparse.internal.Instrument {
+ def beforeParse(parser: String, index: Int): Unit = ()
+ def afterParse(parser: String, index: Int, success: Boolean): Unit = {
+ if (index > furthest) furthest = index
+ }
+ }
+
+ parse(code, Splitter(_), instrument = instrument) match{
+ case Parsed.Failure(_, index, extra) if furthest == code.length => None
+ case f @ Parsed.Failure(_, _, _) => Some(Left(
+ formatFastparseError(fileName, code, f)
+ ))
+ case Parsed.Success(value, index) => Some(Right(value.map(_._2)))
+ }
+ } else
+ parse(code, Splitter(_)) match{
+ case f @ Parsed.Failure(_, _, _) => Some(Left(
+ formatFastparseError(fileName, code, f)
+ ))
+ case Parsed.Success(value, index) => Some(Right(value.map(_._2)))
+ }
+
+ def isObjDef(code: String): Boolean = {
+ parse(code, ObjParser(_))
+ .fold((_, _, _) => false, (_, _) => true)
+ }
+
+ private def Separator[_: P] = P( WL ~ "@" ~~ CharIn(" \n\r").rep(1) )
+ private def CompilationUnit[_: P] = P( WL.! ~ StatementBlock(Separator) ~ WL )
+ private def ScriptSplitter[_: P] = P( CompilationUnit.repX(1, Separator) ~ End)
+ def splitScript(code: String): Parsed[Seq[(String, Seq[(Int, String)])]] =
+ parse(code, ScriptSplitter(_))
+ private def ScriptSplitterWithStart[_: P] =
+ P( Start ~ (Index ~ CompilationUnit).repX(1, Separator) ~ End)
+ def splitScriptWithStart(code: String): Parsed[Seq[(Int, (String, Seq[(Int, String)]))]] =
+ parse(code, ScriptSplitterWithStart(_))
+
+ def stringWrap(s: String): String = "\"" + pprint.Util.literalize(s) + "\""
+ def stringSymWrap(s: String): String = {
+ def idToEnd[_: P] = P( scalaparse.syntax.Identifiers.Id ~ End )
+ if (s == "") "'"
+ else parse(s, idToEnd(_)) match{
+ case Parsed.Success(v, _) => "'" + s
+ case f: Parsed.Failure => stringWrap(s)
+ }
+ }
+ def parseImportHooksWithIndices(
+ source: CodeSource,
+ stmts: Seq[(Int, String)]
+ ): (Seq[String], Seq[ImportTree]) = synchronized{
+ val hookedStmts = mutable.Buffer.empty[String]
+ val importTrees = mutable.Buffer.empty[ImportTree]
+ for((startIdx, stmt) <- stmts) {
+ // Call `fastparse.ParserInput.fromString` explicitly, to avoid generating a
+ // lambda in the class body and making the we-do-not-load-fastparse-on-cached-scripts
+ // test fail
+ parse(fastparse.ParserInput.fromString(stmt), ImportSplitter(_)) match{
+ case f: Parsed.Failure => hookedStmts.append(stmt)
+ case Parsed.Success(parsedTrees, _) =>
+ var currentStmt = stmt
+ for(importTree <- parsedTrees){
+ if (importTree.prefix(0)(0) == '$') {
+ val length = importTree.end - importTree.start
+ currentStmt = currentStmt.patch(
+ importTree.start, (importTree.prefix(0) + ".$").padTo(length, ' '), length
+ )
+ val importTree0 = importTree.copy(
+ start = startIdx + importTree.start,
+ end = startIdx + importTree.end
+ )
+ importTrees.append(importTree0)
+ }
+ }
+ hookedStmts.append(currentStmt)
+ }
+ }
+ (hookedStmts.toSeq, importTrees.toSeq)
+ }
+ def formatFastparseError(fileName: String, rawCode: String, f: Parsed.Failure) = {
+
+ val lineColIndex = f.extra.input.prettyIndex(f.index)
+ val expected = f.trace().failure.label
+ val locationString = {
+ val (first, last) = rawCode.splitAt(f.index)
+ val lastSnippet = last.split(newLine).headOption.getOrElse("")
+ val firstSnippet = first.reverse
+ .split(newLine.reverse)
+ .lift(0).getOrElse("").reverse
+ firstSnippet + lastSnippet + newLine + (" " * firstSnippet.length) + "^"
+ }
+ s"$fileName:$lineColIndex expected $expected$newLine$locationString"
+ }
+
+
+ /**
+ * Splits up a script file into its constituent blocks, each of which
+ * is a tuple of (leading-whitespace, statements). Leading whitespace
+ * is returned separately so we can later manipulate the statements e.g.
+ * by adding `val res2 = ` without the whitespace getting in the way
+ */
+ def splitScript(
+ rawCode: String,
+ fileName: String
+ ): Either[String, IndexedSeq[(String, Seq[String])]] = {
+ parse(rawCode, ScriptSplitter(_)) match {
+ case f: Parsed.Failure =>
+ Left(formatFastparseError(fileName, rawCode, f))
+
+ case s: Parsed.Success[Seq[(String, Seq[(Int, String)])]] =>
+
+ var offset = 0
+ val blocks = mutable.ArrayBuffer[(String, Seq[String])]()
+
+ // comment holds comments or empty lines above the code which is not caught along with code
+ for( (comment, codeWithStartIdx) <- s.value){
+ val code = codeWithStartIdx.map(_._2)
+
+ //ncomment has required number of newLines appended based on OS and offset
+ //since fastparse has hardcoded `\n`s, while parsing strings with `\r\n`s it
+ //gives out one extra `\r` after '@' i.e. block change
+ //which needs to be removed to get correct line number (It adds up one extra line)
+ //thats why the `comment.substring(1)` thing is necessary
+ val ncomment =
+ if(windowsPlatform && blocks.nonEmpty && !comment.isEmpty){
+ comment.substring(1) + newLine * offset
+ }else{
+ comment + newLine * offset
+ }
+
+ // 1 is added as Separator parser eats up the newLine char following @
+ offset = offset + (comment.split(newLine, -1).length - 1) +
+ code.map(_.split(newLine, -1).length - 1).sum + 1
+ blocks.append((ncomment, code))
+ }
+
+ Right(blocks.toIndexedSeq)
+ }
+ }
+
+ def splitScriptWithStart(
+ rawCode: String,
+ fileName: String
+ ): Either[Parsed.Failure, IndexedSeq[(Int, String, Seq[(Int, String)])]] = {
+ Parsers.splitScriptWithStart(rawCode) match {
+ case f: Parsed.Failure =>
+ Left(f)
+
+ case Parsed.Success(value, _) =>
+ val blocks = value.toVector.map {
+ case (startIdx, (comment, code)) =>
+ (startIdx, comment, code)
+ }
+ Right(blocks)
+ }
+ }
+
+ def scriptBlocksWithStartIndices(
+ rawCode: String,
+ fileName: String
+ ): Either[IParser.ScriptSplittingError, Seq[IParser.ScriptBlock]] = {
+ splitScriptWithStart(rawCode, fileName) match {
+ case Left(f) =>
+ Left(new IParser.ScriptSplittingError(
+ formatFastparseError(fileName, rawCode, f),
+ f.index,
+ f.trace().failure.label
+ ))
+ case Right(blocks) =>
+ val blocks0 = blocks.map {
+ case (startIdx, ncomment, code) =>
+ IParser.ScriptBlock(startIdx, ncomment, code)
+ }
+ Right(blocks0)
+ }
+ }
+
+ def defaultHighlight(buffer: Vector[Char],
+ comment: fansi.Attrs,
+ `type`: fansi.Attrs,
+ literal: fansi.Attrs,
+ keyword: fansi.Attrs,
+ reset: fansi.Attrs) = {
+ Highlighter.defaultHighlight0(Splitter(_), buffer, comment, `type`, literal, keyword, reset)
+ }
+ def defaultHighlightIndices(buffer: Vector[Char],
+ comment: fansi.Attrs,
+ `type`: fansi.Attrs,
+ literal: fansi.Attrs,
+ keyword: fansi.Attrs,
+ reset: fansi.Attrs) = Highlighter.defaultHighlightIndices0(
+ Splitter(_), buffer, comment, `type`, literal, keyword, reset
+ )
+}
diff --git a/amm/interp/src/main/scala/ammonite/interp/Pressy.scala b/amm/compiler/src/main/scala/ammonite/compiler/Pressy.scala
similarity index 99%
rename from amm/interp/src/main/scala/ammonite/interp/Pressy.scala
rename to amm/compiler/src/main/scala/ammonite/compiler/Pressy.scala
index 70b215869..6ec681b4f 100644
--- a/amm/interp/src/main/scala/ammonite/interp/Pressy.scala
+++ b/amm/compiler/src/main/scala/ammonite/compiler/Pressy.scala
@@ -1,8 +1,8 @@
-package ammonite.interp
+package ammonite.compiler
import java.io.{OutputStream, PrintWriter}
-import ammonite.interp.MakeReporter.makeReporter
+import ammonite.compiler.MakeReporter.makeReporter
import ammonite.util.Name
import scala.reflect.internal.util.{BatchSourceFile, OffsetPosition, Position}
diff --git a/amm/interp/src/main/scala/ammonite/interp/internal/CustomURLZipArchive.scala b/amm/compiler/src/main/scala/ammonite/compiler/internal/CustomURLZipArchive.scala
similarity index 99%
rename from amm/interp/src/main/scala/ammonite/interp/internal/CustomURLZipArchive.scala
rename to amm/compiler/src/main/scala/ammonite/compiler/internal/CustomURLZipArchive.scala
index 3710696a5..0459ad8f3 100644
--- a/amm/interp/src/main/scala/ammonite/interp/internal/CustomURLZipArchive.scala
+++ b/amm/compiler/src/main/scala/ammonite/compiler/internal/CustomURLZipArchive.scala
@@ -1,4 +1,4 @@
-package ammonite.interp.internal
+package ammonite.compiler.internal
import java.io.{ByteArrayInputStream, InputStream}
import java.util.zip.{ZipEntry, ZipFile, ZipInputStream}
diff --git a/amm/repl/src/main/scala/ammonite/repl/tools/HighlightJava.scala b/amm/compiler/src/main/scala/ammonite/compiler/tools/HighlightJava.scala
similarity index 97%
rename from amm/repl/src/main/scala/ammonite/repl/tools/HighlightJava.scala
rename to amm/compiler/src/main/scala/ammonite/compiler/tools/HighlightJava.scala
index 032230feb..c9b372f56 100644
--- a/amm/repl/src/main/scala/ammonite/repl/tools/HighlightJava.scala
+++ b/amm/compiler/src/main/scala/ammonite/compiler/tools/HighlightJava.scala
@@ -1,6 +1,6 @@
-package ammonite.repl.tools
+package ammonite.compiler.tools
-import ammonite.repl.Highlighter.flattenIndices
+import ammonite.compiler.Highlighter.flattenIndices
import ammonite.util.CodeColors
import com.github.javaparser.GeneratedJavaParserConstants._
import com.github.javaparser.{GeneratedJavaParserConstants, ParseStart, StringProvider}
diff --git a/amm/repl/src/main/scala/ammonite/repl/tools/SourceRuntime.scala b/amm/compiler/src/main/scala/ammonite/compiler/tools/SourceRuntime.scala
similarity index 94%
rename from amm/repl/src/main/scala/ammonite/repl/tools/SourceRuntime.scala
rename to amm/compiler/src/main/scala/ammonite/compiler/tools/SourceRuntime.scala
index 7b1e42c32..0857a88c1 100644
--- a/amm/repl/src/main/scala/ammonite/repl/tools/SourceRuntime.scala
+++ b/amm/compiler/src/main/scala/ammonite/compiler/tools/SourceRuntime.scala
@@ -1,13 +1,12 @@
-package ammonite.repl.tools
+package ammonite.compiler.tools
import javassist.{ByteArrayClassPath, CtClass, CtMethod}
-import ammonite.repl.Highlighter
-import ammonite.repl.api.Location
+import ammonite.compiler.Highlighter
import ammonite.runtime.tools.browse.Strings
import ammonite.util.CodeColors
-import ammonite.util.Util.newLine
+import ammonite.util.Util.{Location, newLine}
import scala.collection.mutable
import scala.language.experimental.macros
@@ -252,5 +251,23 @@ object SourceRuntime{
}
}
+ def failLoudly[T](res: Either[String, T]): T = res match{
+ case Left(s) => throw new Exception(s)
+ case Right(r) => r
+ }
+
+ def desugarImpl(s: String)(implicit colors: ammonite.util.CodeColors): Desugared = {
+
+ new Desugared(
+ ammonite.compiler.Parsers.defaultHighlight(
+ s.toVector,
+ colors.comment,
+ colors.`type`,
+ colors.literal,
+ colors.keyword,
+ fansi.Attr.Reset
+ ).mkString
+ )
+ }
}
diff --git a/amm/repl/api/src/main/scala/ammonite/repl/tools/desugar.scala b/amm/compiler/src/main/scala/ammonite/compiler/tools/desugar.scala
similarity index 72%
rename from amm/repl/api/src/main/scala/ammonite/repl/tools/desugar.scala
rename to amm/compiler/src/main/scala/ammonite/compiler/tools/desugar.scala
index 4aea3f1e0..88150b6eb 100644
--- a/amm/repl/api/src/main/scala/ammonite/repl/tools/desugar.scala
+++ b/amm/compiler/src/main/scala/ammonite/compiler/tools/desugar.scala
@@ -1,4 +1,4 @@
-package ammonite.repl.tools
+package ammonite.compiler.tools
import sourcecode.Compat._
import scala.language.experimental.macros
@@ -11,7 +11,7 @@ object desugar{
def transformer(c: Context)(expr: c.Expr[Any]): c.Expr[Desugared] = {
import c.universe._
c.Expr[Desugared](
- q"ammonite.repl.api.SourceBridge.value.desugarImpl(${c.universe.showCode(expr.tree)})"
+ q"ammonite.compiler.tools.SourceRuntime.desugarImpl(${c.universe.showCode(expr.tree)})"
)
}
diff --git a/amm/repl/api/src/main/scala/ammonite/repl/tools/source.scala b/amm/compiler/src/main/scala/ammonite/compiler/tools/source.scala
similarity index 96%
rename from amm/repl/api/src/main/scala/ammonite/repl/tools/source.scala
rename to amm/compiler/src/main/scala/ammonite/compiler/tools/source.scala
index d00cc2a06..89c694f0d 100644
--- a/amm/repl/api/src/main/scala/ammonite/repl/tools/source.scala
+++ b/amm/compiler/src/main/scala/ammonite/compiler/tools/source.scala
@@ -1,8 +1,8 @@
-package ammonite.repl.tools
+package ammonite.compiler.tools
-import ammonite.repl.api.Location
import ammonite.runtime.tools.browse.Strings
import ammonite.util.CodeColors
+import ammonite.util.Util.Location
import sourcecode.Compat._
import scala.annotation.tailrec
@@ -53,7 +53,7 @@ object source{
colors: c.Expr[CodeColors]): c.Expr[Unit] = {
import c.universe._
val defaultBrowseExpr = c.Expr[Int => Strings](
- q"_root_.ammonite.repl.api.SourceBridge.value.browseSourceCommand"
+ q"${prefix(c)}.browseSourceCommand"
)
applyCustomizeCommandMacro(c)(f, defaultBrowseExpr)(pprinter, colors)
@@ -86,7 +86,7 @@ object source{
def prefix(c: Context) = {
import c.universe._
- q"ammonite.repl.api.SourceBridge.value"
+ q"ammonite.compiler.tools.SourceRuntime"
}
/**
* Attempts to up an expression, into either a LHS + methodcall + rhs. We
diff --git a/amm/interp/src/main/scala/scala/tools/nsc/CustomZipAndJarFileLookupFactory.scala b/amm/compiler/src/main/scala/scala/tools/nsc/CustomZipAndJarFileLookupFactory.scala
similarity index 96%
rename from amm/interp/src/main/scala/scala/tools/nsc/CustomZipAndJarFileLookupFactory.scala
rename to amm/compiler/src/main/scala/scala/tools/nsc/CustomZipAndJarFileLookupFactory.scala
index b2dbb3673..7a94d07b9 100644
--- a/amm/interp/src/main/scala/scala/tools/nsc/CustomZipAndJarFileLookupFactory.scala
+++ b/amm/compiler/src/main/scala/scala/tools/nsc/CustomZipAndJarFileLookupFactory.scala
@@ -3,7 +3,7 @@ package scala.tools.nsc
import java.io.File
import java.net.URL
-import ammonite.interp.internal.CustomURLZipArchive
+import ammonite.compiler.internal.CustomURLZipArchive
import scala.reflect.io.AbstractFile
import scala.tools.nsc.classpath.FileUtils.AbstractFileOps
@@ -13,7 +13,7 @@ import scala.tools.nsc.util.{ClassPath, ClassRepresentation}
// Originally based on
// https://github.com/scala/scala/blob/329deac9ab4f39e5e766ec3ab3f3f4cddbc44aa1
// /src/compiler/scala/tools/nsc/classpath/ZipAndJarFileLookupFactory.scala#L50-L166,
-// then adapted to rely on ammonite.interp.internal.CustomURLZipArchive (accepting URLs)
+// then adapted to rely on ammonite.compiler.internal.CustomURLZipArchive (accepting URLs)
// rather than FileZipArchive (only accepting files).
object CustomZipAndJarFileLookupFactory {
diff --git a/amm/interp/api/src/main/scala/ammonite/interp/api/InterpAPI.scala b/amm/interp/api/src/main/scala/ammonite/interp/api/InterpAPI.scala
index d40b89664..8db33a3c6 100644
--- a/amm/interp/api/src/main/scala/ammonite/interp/api/InterpAPI.scala
+++ b/amm/interp/api/src/main/scala/ammonite/interp/api/InterpAPI.scala
@@ -60,18 +60,8 @@ trait InterpAPI {
* exitValue before the repl exits
*/
val beforeExitHooks: mutable.Buffer[Any => Any]
- /**
- * Configures the current compiler, or if the compiler hasn't been initialized
- * yet, registers the configuration callback and applies it to the compiler
- * when it ends up being initialized later
- */
- def configureCompiler(c: scala.tools.nsc.Global => Unit): Unit
- /**
- * Pre-configures the next compiler. Useful for tuning options that are
- * used during parsing such as -Yrangepos
- */
- def preConfigureCompiler(c: scala.tools.nsc.Settings => Unit): Unit
+ def _compilerManager: ammonite.compiler.iface.CompilerLifecycleManager
}
diff --git a/amm/interp/src/main/scala/ammonite/interp/Interpreter.scala b/amm/interp/src/main/scala/ammonite/interp/Interpreter.scala
index b23943edf..d2e572974 100644
--- a/amm/interp/src/main/scala/ammonite/interp/Interpreter.scala
+++ b/amm/interp/src/main/scala/ammonite/interp/Interpreter.scala
@@ -3,18 +3,22 @@ package ammonite.interp
import java.io.{File, OutputStream, PrintStream}
import java.util.regex.Pattern
+import ammonite.compiler.iface.{
+ CodeWrapper,
+ CompilerBuilder,
+ CompilerLifecycleManager,
+ Parser,
+ Preprocessor
+}
import ammonite.interp.api.{InterpAPI, InterpLoad, LoadJar}
-import ammonite.interp.CodeWrapper
import scala.collection.mutable
import ammonite.runtime._
-import fastparse._
import annotation.tailrec
import ammonite.runtime.tools.IvyThing
-import ammonite.util.ImportTree
import ammonite.util.Util._
-import ammonite.util._
+import ammonite.util.{Frame => _, _}
import coursierapi.{Dependency, Fetch, Repository}
/**
@@ -22,7 +26,10 @@ import coursierapi.{Dependency, Fetch, Repository}
* to interpret Scala code. Doesn't attempt to provide any
* real encapsulation for now.
*/
-class Interpreter(val printer: Printer,
+class Interpreter(compilerBuilder: CompilerBuilder,
+ // by-name, so that fastparse isn't loaded when we don't need it
+ parser: => Parser,
+ val printer: Printer,
val storage: Storage,
val wd: os.Path,
colors: Ref[Colors],
@@ -53,8 +60,8 @@ class Interpreter(val printer: Printer,
def dependencyComplete: String => (Int, Seq[String]) =
IvyThing.completer(repositories(), verbose = verboseOutput)
- val compilerManager = new CompilerLifecycleManager(
- storage,
+ val compilerManager = compilerBuilder.newManager(
+ storage.dirOpt.map(_.toNIO),
headFrame,
Some(dependencyComplete),
classPathWhitelist,
@@ -237,7 +244,7 @@ class Interpreter(val printer: Printer,
Seq(Name("ammonite"), Name("$sess")),
Some(wd/"(console)")
)
- val (hookStmts, importTrees) = Parsers.parseImportHooks(codeSource, stmts)
+ val (hookStmts, importTrees) = parser.parseImportHooks(codeSource, stmts)
for{
_ <- Catching { case ex => Res.Exception(ex, "") }
@@ -278,10 +285,13 @@ class Interpreter(val printer: Printer,
incrementLine: () => Unit): Res[(Evaluated, Tag)] = synchronized{
for{
_ <- Catching{ case e: ThreadDeath => Evaluator.interrupted(e) }
- output <- compilerManager.compileClass(
- processed,
- printer,
- fileName
+ output <- Res(
+ compilerManager.compileClass(
+ processed,
+ printer,
+ fileName
+ ),
+ "Compilation Failed"
)
_ = incrementLine()
res <- eval.processLine(
@@ -314,8 +324,11 @@ class Interpreter(val printer: Printer,
for {
_ <- Catching{case e: Throwable => e.printStackTrace(); throw e}
- output <- compilerManager.compileClass(
- processed, printer, codeSource.fileName
+ output <- Res(
+ compilerManager.compileClass(
+ processed, printer, codeSource.fileName
+ ),
+ "Compilation Failed"
)
cls <- eval.loadClass(fullyQualifiedName, output.classFiles)
@@ -371,7 +384,7 @@ class Interpreter(val printer: Printer,
// and none of it's blocks end up needing to be re-compiled. We don't know up
// front if any blocks will need re-compilation, because it may import $file
// another script which gets changed, and we'd only know when we reach that block
- lazy val splittedScript = Preprocessor.splitScript(
+ lazy val splittedScript = parser.splitScript(
Interpreter.skipSheBangLine(code),
codeSource.fileName
)
@@ -416,7 +429,7 @@ class Interpreter(val printer: Printer,
val wrapperName = Name("cmd" + currentLine)
val fileName = wrapperName.encoded + ".sc"
for {
- blocks <- Res(Preprocessor.splitScript(Interpreter.skipSheBangLine(code), fileName))
+ blocks <- Res(parser.splitScript(Interpreter.skipSheBangLine(code), fileName))
metadata <- processAllScriptBlocks(
blocks.map(_ => None),
@@ -565,7 +578,7 @@ class Interpreter(val printer: Printer,
for{
allSplittedChunks <- splittedScript
(leadingSpaces, stmts) = allSplittedChunks(wrapperIndex - 1)
- (hookStmts, importTrees) = Parsers.parseImportHooks(codeSource, stmts)
+ (hookStmts, importTrees) = parser.parseImportHooks(codeSource, stmts)
hookInfo <- resolveImportHooks(
importTrees, hookStmts, codeSource, scriptCodeWrapper.wrapperPath
)
@@ -654,14 +667,6 @@ class Interpreter(val printer: Printer,
def watch(p: os.Path) = interp.watch(p)
def watchValue[T](v: => T): T = {interp.watchValue(v); v}
- def configureCompiler(callback: scala.tools.nsc.Global => Unit) = {
- compilerManager.configureCompiler(callback)
- }
-
- def preConfigureCompiler(callback: scala.tools.nsc.Settings => Unit) = {
- compilerManager.preConfigureCompiler(callback)
- }
-
val beforeExitHooks = interp.beforeExitHooks
val repositories = interp.repositories
@@ -701,6 +706,8 @@ class Interpreter(val printer: Printer,
}
}
+
+ def _compilerManager = interp.compilerManager
}
}
@@ -713,9 +720,13 @@ object Interpreter{
"ammonite.interp.api.IvyConstructor.{ArtifactIdExt, GroupIdExt}",
importType = ImportData.Type
),
+ ImportData("""ammonite.compiler.CompilerExtensions.{
+ CompilerInterpAPIExtensions,
+ CompilerReplAPIExtensions
+ }"""),
ImportData("ammonite.runtime.tools.{browse, grep, time}"),
ImportData("ammonite.runtime.tools.tail", importType = ImportData.TermType),
- ImportData("ammonite.repl.tools.{desugar, source}"),
+ ImportData("ammonite.compiler.tools.{desugar, source}"),
ImportData("mainargs.{arg, main}"),
ImportData("ammonite.repl.tools.Util.PathRead")
)
diff --git a/amm/interp/src/main/scala/ammonite/interp/Parsers.scala b/amm/interp/src/main/scala/ammonite/interp/Parsers.scala
deleted file mode 100644
index d451191a1..000000000
--- a/amm/interp/src/main/scala/ammonite/interp/Parsers.scala
+++ /dev/null
@@ -1,145 +0,0 @@
-package ammonite.interp
-
-import ammonite.util.ImportTree
-import ammonite.util.Util.CodeSource
-
-import scala.collection.mutable
-
-object Parsers {
-
- import fastparse._
-
- import ScalaWhitespace._
- import scalaparse.Scala._
-
- // For some reason Scala doesn't import this by default
- def `_`[_: P] = scalaparse.Scala.`_`
-
-
- def ImportSplitter[_: P]: P[Seq[ammonite.util.ImportTree]] = {
- def IdParser = P( (Id | `_` ).! ).map(
- s => if (s(0) == '`') s.drop(1).dropRight(1) else s
- )
- def Selector = P( IdParser ~ (`=>` ~/ IdParser).? )
- def Selectors = P( "{" ~/ Selector.rep(sep = ","./) ~ "}" )
- def BulkImport = P( `_`).map(
- _ => Seq("_" -> None)
- )
- def Prefix = P( IdParser.rep(1, sep = ".") )
- def Suffix = P( "." ~/ (BulkImport | Selectors) )
- def ImportExpr: P[ammonite.util.ImportTree] = {
- // Manually use `WL0` parser here, instead of relying on WhitespaceApi, as
- // we do not want the whitespace to be consumed even if the WL0 parser parses
- // to the end of the input (which is the default behavior for WhitespaceApi)
- P( Index ~~ Prefix ~~ (WL0 ~~ Suffix).? ~~ Index).map{
- case (start, idSeq, selectors, end) =>
- ammonite.util.ImportTree(idSeq, selectors, start, end)
- }
- }
- P( `import` ~/ ImportExpr.rep(1, sep = ","./) )
- }
-
- def PatVarSplitter[_: P] = {
- def Prefixes = P(Prelude ~ (`var` | `val`))
- def Lhs = P( Prefixes ~/ BindPattern.rep(1, "," ~/ Pass) ~ (`:` ~/ Type).? )
- P( Lhs.! ~ (`=` ~/ WL ~ StatCtx.Expr.!) ~ End )
- }
- def patVarSplit(code: String) = {
- val Parsed.Success((lhs, rhs), _) = parse(code, PatVarSplitter(_))
- (lhs, rhs)
- }
-
- def Prelude[_: P] = P( (Annot ~ OneNLMax).rep ~ (Mod ~/ Pass).rep )
-
- def TmplStat[_: P] = P( Import | Prelude ~ BlockDef | StatCtx.Expr )
-
-
- // Do this funny ~~WS thing to make sure we capture the whitespace
- // together with each statement; otherwise, by default, it gets discarded.
- //
- // After each statement, there must either be `Semis`, a "}" marking the
- // end of the block, or the `End` of the input
- def StatementBlock[_: P](blockSep: => P0) =
- P( Semis.? ~ (Index ~ (!blockSep ~ TmplStat ~~ WS ~~ (Semis | &("}") | End)).!).repX)
-
- def Splitter0[_: P] = P( StatementBlock(Fail) )
- def Splitter[_: P] = P( ("{" ~ Splitter0 ~ "}" | Splitter0) ~ End )
-
- def ObjParser[_: P] = P( ObjDef )
-
- /**
- * Attempts to break a code blob into multiple statements. Returns `None` if
- * it thinks the code blob is "incomplete" and requires more input
- */
- def split(code: String): Option[Parsed[Seq[String]]] = {
- // We use `instrument` to detect when the parser has reached the end of the
- // input, any time during the parse. If it has done so, and failed, we
- // consider the input incomplete.
- var furthest = 0
- val instrument = new fastparse.internal.Instrument {
- def beforeParse(parser: String, index: Int): Unit = ()
- def afterParse(parser: String, index: Int, success: Boolean): Unit = {
- if (index > furthest) furthest = index
- }
- }
-
- parse(code, Splitter(_), instrument = instrument) match{
- case Parsed.Failure(_, index, extra) if furthest == code.length => None
- case f @ Parsed.Failure(_, _, _) => Some(f)
- case Parsed.Success(value, index) => Some(Parsed.Success(value.map(_._2), index))
- }
- }
-
- def isObjDef(code: String): Boolean = {
- parse(code, ObjParser(_))
- .fold((_, _, _) => false, (_, _) => true)
- }
-
- def Separator[_: P] = P( WL ~ "@" ~~ CharIn(" \n\r").rep(1) )
- def CompilationUnit[_: P] = P( WL.! ~ StatementBlock(Separator) ~ WL )
- def ScriptSplitter[_: P] = P( CompilationUnit.repX(1, Separator) ~ End)
- def splitScript(code: String) = parse(code, ScriptSplitter(_))
- def ScriptSplitterWithStart[_: P] = P( Start ~ (Index ~ CompilationUnit).repX(1, Separator) ~ End)
- def splitScriptWithStart(code: String) = parse(code, ScriptSplitterWithStart(_))
-
- def stringWrap(s: String) = "\"" + pprint.Util.literalize(s) + "\""
- def stringSymWrap(s: String) = {
- def idToEnd[_: P] = P( scalaparse.syntax.Identifiers.Id ~ End )
- if (s == "") "'"
- else parse(s, idToEnd(_)) match{
- case Parsed.Success(v, _) => "'" + s
- case f: Parsed.Failure => stringWrap(s)
- }
- }
- def parseImportHooks(source: CodeSource, stmts: Seq[String]) =
- parseImportHooksWithIndices(source, stmts.map((0, _)))
- def parseImportHooksWithIndices(source: CodeSource, stmts: Seq[(Int, String)]) = synchronized{
- val hookedStmts = mutable.Buffer.empty[String]
- val importTrees = mutable.Buffer.empty[ImportTree]
- for((startIdx, stmt) <- stmts) {
- // Call `fastparse.ParserInput.fromString` explicitly, to avoid generating a
- // lambda in the class body and making the we-do-not-load-fastparse-on-cached-scripts
- // test fail
- parse(fastparse.ParserInput.fromString(stmt), Parsers.ImportSplitter(_)) match{
- case f: Parsed.Failure => hookedStmts.append(stmt)
- case Parsed.Success(parsedTrees, _) =>
- var currentStmt = stmt
- for(importTree <- parsedTrees){
- if (importTree.prefix(0)(0) == '$') {
- val length = importTree.end - importTree.start
- currentStmt = currentStmt.patch(
- importTree.start, (importTree.prefix(0) + ".$").padTo(length, ' '), length
- )
- val importTree0 = importTree.copy(
- start = startIdx + importTree.start,
- end = startIdx + importTree.end
- )
- importTrees.append(importTree0)
- }
- }
- hookedStmts.append(currentStmt)
- }
- }
- (hookedStmts.toSeq, importTrees.toSeq)
- }
-}
diff --git a/amm/interp/src/main/scala/ammonite/interp/PredefInitialization.scala b/amm/interp/src/main/scala/ammonite/interp/PredefInitialization.scala
index 6f0f54f80..cc734cbc7 100644
--- a/amm/interp/src/main/scala/ammonite/interp/PredefInitialization.scala
+++ b/amm/interp/src/main/scala/ammonite/interp/PredefInitialization.scala
@@ -1,7 +1,7 @@
package ammonite.interp
-import ammonite.interp.api.{APIHolder, InterpAPI}
+import ammonite.interp.api.InterpAPI
import ammonite.runtime.{SpecialClassLoader, Storage}
import ammonite.util.ScriptOutput.Metadata
import ammonite.util.{ImportData, Imports, Name, PredefInfo, Res}
diff --git a/amm/interp/src/main/scala/ammonite/interp/Preprocessor.scala b/amm/interp/src/main/scala/ammonite/interp/Preprocessor.scala
deleted file mode 100644
index af8c60bc6..000000000
--- a/amm/interp/src/main/scala/ammonite/interp/Preprocessor.scala
+++ /dev/null
@@ -1,153 +0,0 @@
-package ammonite.interp
-
-import ammonite.util._
-import ammonite.util.Util.{CodeSource, newLine, normalizeNewlines, windowsPlatform}
-import fastparse._
-
-import scala.tools.nsc.{Global => G}
-import collection.mutable
-/**
- * Responsible for all scala-source-code-munging that happens within the
- * Ammonite REPL.
- *
- * Performs several tasks:
- *
- * - Takes top-level Scala expressions and assigns them to `res{1, 2, 3, ...}`
- * values so they can be accessed later in the REPL
- *
- * - Wraps the code snippet with an wrapper `object` since Scala doesn't allow
- * top-level expressions
- *
- * - Mangles imports from our [[ammonite.util.ImportData]] data structure into a source
- * String
- *
- * - Combines all of these into a complete compilation unit ready to feed into
- * the Scala compiler
- */
-trait Preprocessor{
- def transform(stmts: Seq[String],
- resultIndex: String,
- leadingSpaces: String,
- codeSource: CodeSource,
- indexedWrapperName: Name,
- imports: Imports,
- printerTemplate: String => String,
- extraCode: String,
- skipEmpty: Boolean,
- markScript: Boolean,
- codeWrapper: CodeWrapper): Res[Preprocessor.Output]
-}
-
-object Preprocessor{
- case class Output(code: String,
- prefixCharLength: Int,
- userCodeNestingLevel: Int)
-
-
- def formatFastparseError(fileName: String, rawCode: String, f: Parsed.Failure) = {
-
- val lineColIndex = f.extra.input.prettyIndex(f.index)
- val expected = f.trace().failure.label
- val locationString = {
- val (first, last) = rawCode.splitAt(f.index)
- val lastSnippet = last.split(newLine).headOption.getOrElse("")
- val firstSnippet = first.reverse
- .split(newLine.reverse)
- .lift(0).getOrElse("").reverse
- firstSnippet + lastSnippet + newLine + (" " * firstSnippet.length) + "^"
- }
- s"$fileName:$lineColIndex expected $expected$newLine$locationString"
- }
-
-
- /**
- * Splits up a script file into its constituent blocks, each of which
- * is a tuple of (leading-whitespace, statements). Leading whitespace
- * is returned separately so we can later manipulate the statements e.g.
- * by adding `val res2 = ` without the whitespace getting in the way
- */
- def splitScript(
- rawCode: String,
- fileName: String
- ): Either[String, IndexedSeq[(String, Seq[String])]] = {
- Parsers.splitScript(rawCode) match {
- case f: Parsed.Failure =>
- Left(formatFastparseError(fileName, rawCode, f))
-
- case s: Parsed.Success[Seq[(String, Seq[(Int, String)])]] =>
-
- var offset = 0
- val blocks = mutable.ArrayBuffer[(String, Seq[String])]()
-
- // comment holds comments or empty lines above the code which is not caught along with code
- for( (comment, codeWithStartIdx) <- s.value){
- val code = codeWithStartIdx.map(_._2)
-
- //ncomment has required number of newLines appended based on OS and offset
- //since fastparse has hardcoded `\n`s, while parsing strings with `\r\n`s it
- //gives out one extra `\r` after '@' i.e. block change
- //which needs to be removed to get correct line number (It adds up one extra line)
- //thats why the `comment.substring(1)` thing is necessary
- val ncomment =
- if(windowsPlatform && blocks.nonEmpty && !comment.isEmpty){
- comment.substring(1) + newLine * offset
- }else{
- comment + newLine * offset
- }
-
- // 1 is added as Separator parser eats up the newLine char following @
- offset = offset + (comment.split(newLine, -1).length - 1) +
- code.map(_.split(newLine, -1).length - 1).sum + 1
- blocks.append((ncomment, code))
- }
-
- Right(blocks.toIndexedSeq)
- }
- }
-
- def splitScriptWithStart(
- rawCode: String,
- fileName: String
- ): Either[Parsed.Failure, IndexedSeq[(Int, String, Seq[(Int, String)])]] = {
- Parsers.splitScriptWithStart(rawCode) match {
- case f: Parsed.Failure =>
- Left(f)
-
- case Parsed.Success(value, _) =>
- val blocks = value.toVector.map {
- case (startIdx, (comment, code)) =>
- (startIdx, comment, code)
- }
- Right(blocks)
- }
- }
-
-
-
- def wrapCode(codeSource: CodeSource,
- indexedWrapperName: Name,
- code: String,
- printCode: String,
- imports: Imports,
- extraCode: String,
- markScript: Boolean,
- codeWrapper: CodeWrapper) = {
-
- //we need to normalize topWrapper and bottomWrapper in order to ensure
- //the snippets always use the platform-specific newLine
- val extraCode0 =
- if (markScript) extraCode + "/**/"
- else extraCode
- val (topWrapper, bottomWrapper, userCodeNestingLevel) =
- codeWrapper(code, codeSource, imports, printCode, indexedWrapperName, extraCode0)
- val (topWrapper0, bottomWrapper0) =
- if (markScript) (topWrapper + "/**/ /**/" + bottomWrapper)
- else (topWrapper, bottomWrapper)
- val importsLen = topWrapper0.length
-
- (topWrapper0 + code + bottomWrapper0, importsLen, userCodeNestingLevel)
- }
-
-
-}
-
diff --git a/amm/interp/src/main/scala/ammonite/interp/script/AmmoniteBuildServer.scala b/amm/interp/src/main/scala/ammonite/interp/script/AmmoniteBuildServer.scala
index 2e4555a5b..27b392acf 100644
--- a/amm/interp/src/main/scala/ammonite/interp/script/AmmoniteBuildServer.scala
+++ b/amm/interp/src/main/scala/ammonite/interp/script/AmmoniteBuildServer.scala
@@ -7,10 +7,11 @@ import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.{CompletableFuture, Executors, ThreadFactory}
import java.util.UUID
+import ammonite.compiler.iface.{CodeWrapper, CompilerBuilder, Parser}
import ammonite.interp.api.InterpAPI
-import ammonite.interp.{CodeWrapper, DependencyLoader}
-import ammonite.runtime.{Classpath, ImportHook, Storage}
-import ammonite.util.{Imports, Printer}
+import ammonite.interp.DependencyLoader
+import ammonite.runtime.{ImportHook, Storage}
+import ammonite.util.{Classpath, Imports, Printer}
import ch.epfl.scala.bsp4j.{Diagnostic => BDiagnostic, Position => BPosition, _}
import coursierapi.{Dependency, Repository}
import org.eclipse.lsp4j.jsonrpc.Launcher
@@ -21,10 +22,12 @@ import scala.util.{Failure, Success}
import scala.util.control.NonFatal
class AmmoniteBuildServer(
+ compilerBuilder: CompilerBuilder,
+ parser: Parser,
+ codeWrapper: CodeWrapper,
initialScripts: Seq[os.Path] = Nil,
initialImports: Imports = AmmoniteBuildServer.defaultImports,
defaultRepositories: Seq[Repository] = Repository.defaults().asScala.toList,
- codeWrapper: CodeWrapper = CodeWrapper,
importHooks: Map[Seq[String], ImportHook] = ImportHook.defaults
) extends BuildServer with ScalaBuildServer with DummyBuildServerImplems {
@@ -52,12 +55,14 @@ class AmmoniteBuildServer(
classOf[InterpAPI].getClassLoader
else
Thread.currentThread().getContextClassLoader
- private def initialClassPath = Classpath.classpath(initialClassLoader, storage)
- .map(_.toURI)
+ private def initialClassPath =
+ Classpath.classpath(initialClassLoader, storage.dirOpt.map(_.toNIO)).map(_.toURI)
private lazy val proc =
withRoot { root =>
ScriptProcessor(
+ parser,
+ codeWrapper,
dependencyLoader,
defaultRepositories,
Seq(
@@ -68,13 +73,13 @@ class AmmoniteBuildServer(
)
),
root,
- codeWrapper,
importHooks
)
}
private lazy val compiler =
withRoot { root =>
new ScriptCompiler(
+ compilerBuilder,
storage,
printer,
codeWrapper,
diff --git a/amm/interp/src/main/scala/ammonite/interp/script/ScriptCompileResult.scala b/amm/interp/src/main/scala/ammonite/interp/script/ScriptCompileResult.scala
index 890f2bf41..f64c57b3a 100644
--- a/amm/interp/src/main/scala/ammonite/interp/script/ScriptCompileResult.scala
+++ b/amm/interp/src/main/scala/ammonite/interp/script/ScriptCompileResult.scala
@@ -1,8 +1,8 @@
package ammonite.interp.script
-import ammonite.interp.{Compiler => AmmCompiler}
+import ammonite.compiler.iface.Compiler.Output
final case class ScriptCompileResult(
diagnostics: Seq[Diagnostic],
- errorOrOutput: Either[String, Seq[AmmCompiler.Output]]
+ errorOrOutput: Either[String, Seq[Output]]
)
diff --git a/amm/interp/src/main/scala/ammonite/interp/script/ScriptCompiler.scala b/amm/interp/src/main/scala/ammonite/interp/script/ScriptCompiler.scala
index 85897aea9..8d5d0ffac 100644
--- a/amm/interp/src/main/scala/ammonite/interp/script/ScriptCompiler.scala
+++ b/amm/interp/src/main/scala/ammonite/interp/script/ScriptCompiler.scala
@@ -2,22 +2,15 @@ package ammonite.interp.script
import java.util.concurrent.ConcurrentHashMap
-import ammonite.interp.{
- CodeWrapper,
- Compiler => AmmCompiler,
- DefaultPreprocessor,
- Interpreter,
- MakeReporter
-}
-import ammonite.runtime.{Classpath, Storage}
-import ammonite.util.{Imports, Name, Printer}
+import ammonite.compiler.iface.{CodeWrapper, Compiler => AmmCompiler, CompilerBuilder}
+import ammonite.runtime.Storage
+import ammonite.util.{Imports, Printer}
import scala.collection.JavaConverters._
import scala.collection.mutable
-import scala.reflect.io.VirtualDirectory
-import scala.tools.nsc.Settings
final class ScriptCompiler(
+ compilerBuilder: CompilerBuilder,
storage: Storage,
printer: Printer, // TODO Remove this
codeWrapper: CodeWrapper,
@@ -84,11 +77,7 @@ final class ScriptCompiler(
module: Script,
dependencies: Script.ResolvedDependencies
): ScriptCompileResult =
- settingsOrError(module) match {
- case Left(errors) => ScriptCompileResult(Nil, Left(errors.mkString(", ")))
- case Right((settings, settingsArgs)) =>
- compileIfNeeded(settings, settingsArgs, module, dependencies)
- }
+ compileIfNeeded(moduleSettings(module), module, dependencies)
/**
* Reads compilation output from cache.
@@ -126,30 +115,11 @@ final class ScriptCompiler(
else
Nil
- private def settingsOrError(module: Script): Either[Seq[String], (Settings, Seq[String])] = {
-
- val args = moduleSettings(module)
-
- val errors = new mutable.ListBuffer[String]
- val settings0 = new Settings(err => errors += err)
- val (_, unparsed) = settings0.processArguments(args, true)
- for (arg <- unparsed)
- errors += s"Unrecognized argument: $arg"
-
- if (errors.isEmpty)
- Right((settings0, args))
- else
- Left(errors.toList)
- }
-
/** Writes on disk the source passed to scalac, corresponding to this script */
def preCompile(module: Script): Unit = {
- val settings = settingsOrError(module)
- .map(_._1)
- .getOrElse(new Settings(_ => ()))
-
val compiler = new SingleScriptCompiler(
+ compilerBuilder,
initialClassLoader,
storage,
printer,
@@ -158,7 +128,7 @@ final class ScriptCompiler(
codeWrapper,
wd,
generateSemanticDbs,
- settings,
+ moduleSettings(module),
module,
Script.ResolvedDependencies(Nil, Nil, Nil),
moduleTarget(module),
@@ -215,7 +185,6 @@ final class ScriptCompiler(
None
private def compileIfNeeded(
- settings0: Settings,
settingsArgs: Seq[String],
script: Script,
dependencies: Script.ResolvedDependencies
@@ -224,20 +193,21 @@ final class ScriptCompiler(
val key = InMemoryCacheKey(settingsArgs, script, dependencies)
Option(cache.get(key)).getOrElse {
cleanUpCache()
- val res = doCompile(settings0, script, dependencies)
+ val res = doCompile(settingsArgs, script, dependencies)
Option(cache.putIfAbsent(key, res))
.getOrElse(res)
}
} else
- doCompile(settings0, script, dependencies)
+ doCompile(settingsArgs, script, dependencies)
private def doCompile(
- settings0: Settings,
+ settingsArgs: Seq[String],
module: Script,
dependencies: Script.ResolvedDependencies
): ScriptCompileResult = {
val compiler = new SingleScriptCompiler(
+ compilerBuilder,
initialClassLoader,
storage,
printer,
@@ -246,7 +216,7 @@ final class ScriptCompiler(
codeWrapper,
wd,
generateSemanticDbs,
- settings0,
+ settingsArgs,
module,
dependencies,
moduleTarget(module),
diff --git a/amm/interp/src/main/scala/ammonite/interp/script/ScriptProcessor.scala b/amm/interp/src/main/scala/ammonite/interp/script/ScriptProcessor.scala
index c309618fc..7043d18da 100644
--- a/amm/interp/src/main/scala/ammonite/interp/script/ScriptProcessor.scala
+++ b/amm/interp/src/main/scala/ammonite/interp/script/ScriptProcessor.scala
@@ -2,7 +2,8 @@ package ammonite.interp.script
import java.io.File
-import ammonite.interp.{CodeWrapper, DependencyLoader, Interpreter, Parsers, Preprocessor}
+import ammonite.compiler.iface.{CodeWrapper, Parser}
+import ammonite.interp.{DependencyLoader, Interpreter}
import ammonite.runtime.{Frame, ImportHook, Storage}
import ammonite.util.{ImportTree, Name, Util}
import ammonite.util.Util.CodeSource
@@ -11,11 +12,12 @@ import coursierapi.{Dependency, Repository}
import scala.collection.mutable
final case class ScriptProcessor(
+ parser: Parser,
+ codeWrapper: CodeWrapper,
dependencyLoader: DependencyLoader,
defaultRepositories: Seq[Repository],
extraPluginDependencies: Seq[Dependency] = Nil,
wd: os.Path = os.pwd,
- codeWrapper: CodeWrapper = CodeWrapper,
importHooks: Map[Seq[String], ImportHook] = ImportHook.defaults
) {
@@ -28,7 +30,7 @@ final case class ScriptProcessor(
val rawCode = Interpreter.skipSheBangLine(code)
lazy val offsetToPos = PositionOffsetConversion.offsetToPos(rawCode)
- val splittedScript = Preprocessor.splitScriptWithStart(
+ val splittedScript = parser.scriptBlocksWithStartIndices(
rawCode,
codeSource.fileName
).left.map { f =>
@@ -38,8 +40,7 @@ final case class ScriptProcessor(
val actualEndIdx = if (endIdx < 0) rawCode.length else endIdx
offsetToPos(actualEndIdx)
}
- val expected = f.trace().failure.label
- Seq(Diagnostic("ERROR", startPos, endPos, s"Expected $expected"))
+ Seq(Diagnostic("ERROR", startPos, endPos, s"Expected ${f.expected}"))
}
def hookFor(tree: ImportTree): Either[Diagnostic, (Seq[String], ImportHook)] = {
@@ -82,9 +83,9 @@ final case class ScriptProcessor(
val blocks = for {
elems <- splittedScript.right.toSeq
- (startIdx, leadingSpaces, statements) <- elems
+ Parser.ScriptBlock(startIdx, leadingSpaces, statements) <- elems
} yield {
- val (statements0, importTrees) = Parsers.parseImportHooksWithIndices(codeSource, statements)
+ val (statements0, importTrees) = parser.parseImportHooksWithIndices(codeSource, statements)
val importResults =
for {
tree <- importTrees
diff --git a/amm/interp/src/main/scala/ammonite/interp/script/SingleScriptCompiler.scala b/amm/interp/src/main/scala/ammonite/interp/script/SingleScriptCompiler.scala
index 1e97b8d59..6445fc2a5 100644
--- a/amm/interp/src/main/scala/ammonite/interp/script/SingleScriptCompiler.scala
+++ b/amm/interp/src/main/scala/ammonite/interp/script/SingleScriptCompiler.scala
@@ -1,19 +1,12 @@
package ammonite.interp.script
-import ammonite.interp.{
- CodeWrapper,
- Compiler => AmmCompiler,
- DefaultPreprocessor,
- Interpreter,
- MakeReporter
-}
-import ammonite.runtime.{Classpath, Frame, Storage}
-import ammonite.util.{Imports, Name, Printer, Res}
+import ammonite.compiler.iface.{CodeWrapper, Compiler, CompilerBuilder}
+import ammonite.compiler.iface.Compiler.{Output => CompilerOutput}
+import ammonite.interp.Interpreter
+import ammonite.runtime.{Frame, Storage}
+import ammonite.util.{Classpath, Imports, Name, Printer, Res}
import scala.collection.mutable
-import scala.reflect.internal.util.{NoPosition, Position => SPosition}
-import scala.reflect.io.VirtualDirectory
-import scala.tools.nsc.Settings
/**
* Helper class to compile a single script
@@ -22,6 +15,7 @@ import scala.tools.nsc.Settings
* discarded right after having called `apply` or `writeSources`.
*/
class SingleScriptCompiler(
+ compilerBuilder: CompilerBuilder,
initialClassLoader: ClassLoader,
storage: Storage,
printer: Printer,
@@ -30,28 +24,13 @@ class SingleScriptCompiler(
codeWrapper: CodeWrapper,
wd: Option[os.Path],
generateSemanticDbs: Boolean,
- settings: Settings,
+ settings: Seq[String],
module: Script,
dependencies: Script.ResolvedDependencies,
moduleTarget: Option[os.Path],
moduleSources: Option[os.Path]
) {
- private val dynamicClasspath = {
- val vd = new VirtualDirectory("(memory)", None)
- AmmCompiler.addToClasspath(dependencies.byteCode, vd)
- vd
- }
-
- private val frame = {
- val f = Frame.createInitial(initialClassLoader)
- f.addClasspath(dependencies.jars.map(_.toNIO.toUri.toURL))
- f.addPluginClasspath(dependencies.pluginJars.map(_.toNIO.toUri.toURL))
- for ((clsName, byteCode) <- dependencies.byteCode)
- f.classloader.addClassFile(clsName, byteCode)
- f
- }
-
private var messages = new mutable.ListBuffer[Diagnostic]
private var newMessages = new mutable.ListBuffer[(String, Int, Int, String)]
private def flushMessages(indexToPos: Int => Position): Unit = {
@@ -64,43 +43,48 @@ class SingleScriptCompiler(
newMessages.clear()
}
- private val compiler = {
-
- val reporter = {
- def add(pos: SPosition, msg: String, severity: String) =
- if (pos == NoPosition)
- newMessages.append((severity, 0, 0, msg))
- else
- newMessages.append((severity, pos.start, pos.end, msg))
- MakeReporter.makeReporter(
- (pos, msg) => add(pos, msg, "ERROR"),
- (pos, msg) => add(pos, msg, "WARNING"),
- (pos, msg) => add(pos, msg, "INFO"),
- settings
- )
+ private val compiler: Compiler = {
+
+ val frame = {
+ val f = Frame.createInitial(initialClassLoader)
+ f.addClasspath(dependencies.jars.map(_.toNIO.toUri.toURL))
+ f.addPluginClasspath(dependencies.pluginJars.map(_.toNIO.toUri.toURL))
+ for ((clsName, byteCode) <- dependencies.byteCode)
+ f.classloader.addClassFile(clsName, byteCode)
+ f
}
- val initialClassPath = Classpath.classpath(initialClassLoader, storage)
- val classPath = Classpath.classpath(frame.classloader, storage)
+ val reporter: CompilerBuilder.Message => Unit = {
+ msg =>
+ newMessages.append((msg.severity, msg.start, msg.end, msg.message))
+ }
- AmmCompiler(
+ val initialClassPath = Classpath.classpath(
+ initialClassLoader,
+ storage.dirOpt.map(_.toNIO)
+ )
+ val classPath = Classpath.classpath(
+ frame.classloader,
+ storage.dirOpt.map(_.toNIO)
+ )
+
+ compilerBuilder.create(
+ initialClassPath,
classPath,
- dynamicClasspath,
+ dependencies.byteCode,
frame.classloader,
frame.pluginClassloader,
- () => (),
Some(reporter),
settings,
classPathWhitelist,
- initialClassPath,
- lineNumberModifier = false
+ false
)
}
private val dependencyImports = initialImports ++ module.dependencyImports
- private val preprocessor = new DefaultPreprocessor(
- compiler.parse(module.codeSource.fileName, _),
+ private val preprocessor = compiler.preprocessor(
+ module.codeSource.fileName,
markGeneratedSections = true
)
@@ -189,7 +173,7 @@ class SingleScriptCompiler(
scriptImports: Imports,
block: Script.Block,
blockIdx: Int
- ): Res[(Imports, Int, String, AmmCompiler.Output)] = {
+ ): Res[(Imports, Int, String, Compiler.Output)] = {
val indexedWrapperName = Interpreter.indexWrapperName(
module.codeSource.wrapperName,
@@ -257,8 +241,8 @@ class SingleScriptCompiler(
} yield (scriptImports ++ output.imports, offset, processedCode, output) // :: acc)
}
- private def compileBlocks(): Res[Seq[(Int, String, AmmCompiler.Output)]] = {
- val start = (Imports(), List.empty[(Int, String, AmmCompiler.Output)])
+ private def compileBlocks(): Res[Seq[(Int, String, CompilerOutput)]] = {
+ val start = (Imports(), List.empty[(Int, String, CompilerOutput)])
val res = Res.fold(start, module.blocks.zipWithIndex) {
case ((scriptImports, acc), (block, blockIdx)) =>
compileBlock(scriptImports, block, blockIdx).map {
diff --git a/amm/repl/api/src/main/scala-2.12/ammonite/repl/api/History.scala b/amm/repl/api/src/main/scala-2.12/ammonite/repl/api/History.scala
index 2efc88ea4..3030b2252 100644
--- a/amm/repl/api/src/main/scala-2.12/ammonite/repl/api/History.scala
+++ b/amm/repl/api/src/main/scala-2.12/ammonite/repl/api/History.scala
@@ -1,9 +1,5 @@
package ammonite.repl.api
-
-import ammonite.util._
-import ammonite.util.Util._
-
import scala.collection.generic.CanBuildFrom
import scala.collection.{IndexedSeqLike, mutable}
diff --git a/amm/repl/api/src/main/scala/ammonite/repl/api/ReplAPI.scala b/amm/repl/api/src/main/scala/ammonite/repl/api/ReplAPI.scala
index a57b6028e..87431ca7a 100644
--- a/amm/repl/api/src/main/scala/ammonite/repl/api/ReplAPI.scala
+++ b/amm/repl/api/src/main/scala/ammonite/repl/api/ReplAPI.scala
@@ -66,16 +66,6 @@ trait ReplAPI {
*/
def newCompiler(): Unit
- /**
- * Access the compiler to do crazy things if you really want to!
- */
- def compiler: scala.tools.nsc.Global
-
- /**
- * Access the presentation compiler to do even crazier things if you really want to!
- */
- def interactiveCompiler: scala.tools.nsc.interactive.Global
-
/**
* Shows all imports added that bring values into scope for the commands a
* user runs; *includes* imports from the built-in predef and user predef files
@@ -162,6 +152,8 @@ trait ReplAPI {
def load: ReplLoad
def clipboard: Clipboard
+
+ def _compilerManager: ammonite.compiler.iface.CompilerLifecycleManager
}
trait ReplLoad{
/**
diff --git a/amm/repl/api/src/main/scala/ammonite/repl/api/SourceAPI.scala b/amm/repl/api/src/main/scala/ammonite/repl/api/SourceAPI.scala
deleted file mode 100644
index 54a1ea815..000000000
--- a/amm/repl/api/src/main/scala/ammonite/repl/api/SourceAPI.scala
+++ /dev/null
@@ -1,45 +0,0 @@
-package ammonite.repl.api
-
-import ammonite.interp.api.APIHolder
-import ammonite.repl.tools.Desugared
-import ammonite.util.CodeColors
-
-case class Location(fileName: String, lineNum: Int, fileContent: String)
-
-trait SourceAPI {
-
- def browseSourceCommand(targetLine: Int) = Seq("less", "+" + targetLine,"-RMN")
-
- def failLoudly[T](res: Either[String, T]): T = res match{
- case Left(s) => throw new Exception(s)
- case Right(r) => r
- }
-
- def loadObjectMemberInfo(symbolOwnerCls: Class[_],
- value: Option[Any],
- memberName: String,
- returnType: Class[_],
- argTypes: Class[_]*): Either[String, Location]
-
- def loadObjectInfo(value: Any): Either[String, Location]
-
- def browseObjectMember(symbolOwnerCls: Class[_],
- value: Option[Any],
- memberName: String,
- pprinter: pprint.PPrinter,
- colors: CodeColors,
- command: Int => Seq[String],
- returnType: Class[_],
- argTypes: Class[_]*): Any
-
- def browseObject(value: Any,
- pprinter: pprint.PPrinter,
- colors: CodeColors,
- command: Int => Seq[String]): Any
-
- def desugarImpl(s: String)(implicit colors: ammonite.util.CodeColors): Desugared
-
-}
-
-object SourceBridge extends APIHolder[SourceAPI]
-
diff --git a/amm/repl/src/main/scala/ammonite/repl/AmmoniteFrontEnd.scala b/amm/repl/src/main/scala/ammonite/repl/AmmoniteFrontEnd.scala
index 1d9ab7408..357c4daef 100644
--- a/amm/repl/src/main/scala/ammonite/repl/AmmoniteFrontEnd.scala
+++ b/amm/repl/src/main/scala/ammonite/repl/AmmoniteFrontEnd.scala
@@ -8,8 +8,11 @@ import GUILikeFilters.SelectionFilter
import ammonite.terminal._
import fastparse.Parsed
import ammonite.util.{Colors, Res}
-import ammonite.interp.{Parsers, Preprocessor}
-case class AmmoniteFrontEnd(extraFilters: Filter = Filter.empty) extends FrontEnd{
+import ammonite.compiler.iface.Parser
+case class AmmoniteFrontEnd(
+ parser: Parser,
+ extraFilters: Filter = Filter.empty
+) extends FrontEnd{
def width = FrontEndUtils.width
def height = FrontEndUtils.height
@@ -26,13 +29,9 @@ case class AmmoniteFrontEnd(extraFilters: Filter = Filter.empty) extends FrontEn
case None => Res.Exit(())
case Some(code) =>
addHistory(code)
- fastparse.parse(code, Parsers.Splitter(_)) match{
- case Parsed.Success(value, idx) =>
- Res.Success((code, value.map(_._2)))
- case f @ Parsed.Failure(_, index, extra) =>
- Res.Failure(
- Preprocessor.formatFastparseError("(console)", code, f)
- )
+ parser.split(code, ignoreIncomplete = false).get match{
+ case Right(value) => Res.Success((code, value))
+ case Left(error) => Res.Failure(error)
}
}
}
@@ -55,7 +54,7 @@ case class AmmoniteFrontEnd(extraFilters: Filter = Filter.empty) extends FrontEn
}
val details2 = for (d <- details) yield {
- Highlighter.defaultHighlight(
+ parser.defaultHighlight(
d.toVector,
colors.comment(),
colors.`type`(),
@@ -100,7 +99,7 @@ case class AmmoniteFrontEnd(extraFilters: Filter = Filter.empty) extends FrontEn
// Enter
val multilineFilter = Filter.action(
SpecialKeys.NewLine,
- ti => Parsers.split(ti.ts.buffer.mkString).isEmpty
+ ti => parser.split(ti.ts.buffer.mkString).isEmpty
){
case TermState(rest, b, c, _) => BasicFilters.injectNewLine(b, c, rest)
}
@@ -133,15 +132,14 @@ case class AmmoniteFrontEnd(extraFilters: Filter = Filter.empty) extends FrontEn
displayTransform = { (buffer, cursor) =>
- val indices = Highlighter.defaultHighlightIndices(
- buffer,
+ val highlighted = fansi.Str(parser.defaultHighlight(
+ buffer.toVector,
colors.comment(),
colors.`type`(),
colors.literal(),
colors.keyword(),
fansi.Attr.Reset
- )
- val highlighted = fansi.Str(Highlighter.flattenIndices(indices, buffer).mkString)
+ ).mkString)
val (newBuffer, offset) = SelectionFilter.mangleBuffer(
selectionFilter, highlighted, cursor, colors.selected()
)
diff --git a/amm/repl/src/main/scala/ammonite/repl/ApiImpls.scala b/amm/repl/src/main/scala/ammonite/repl/ApiImpls.scala
index d21cd0034..d7bbba055 100644
--- a/amm/repl/src/main/scala/ammonite/repl/ApiImpls.scala
+++ b/amm/repl/src/main/scala/ammonite/repl/ApiImpls.scala
@@ -1,11 +1,10 @@
package ammonite.repl
import ammonite.ops.Internals
-import ammonite.repl.api.{Clipboard, FrontEnd, FrontEndAPI, Location, Session, SourceAPI}
-import ammonite.repl.tools.{Desugared, SourceRuntime}
+import ammonite.repl.api.{Clipboard, FrontEnd, FrontEndAPI, Session}
import ammonite.runtime._
import ammonite.util.Util._
-import ammonite.util._
+import ammonite.util.{Frame => _, _}
import java.awt.Toolkit
import java.awt.datatransfer.{DataFlavor, StringSelection}
@@ -138,76 +137,13 @@ object ClipboardImpl extends Clipboard {
}
}
-trait SourceAPIImpl extends SourceAPI {
-
- def loadObjectMemberInfo(symbolOwnerCls: Class[_],
- value: Option[Any],
- memberName: String,
- returnType: Class[_],
- argTypes: Class[_]*): Either[String, Location] =
- SourceRuntime.loadObjectMemberInfo(
- symbolOwnerCls,
- value,
- memberName,
- returnType,
- argTypes: _*
- )
-
- def loadObjectInfo(value: Any): Either[String, Location] =
- SourceRuntime.loadObjectInfo(value)
-
- def browseObjectMember(symbolOwnerCls: Class[_],
- value: Option[Any],
- memberName: String,
- pprinter: pprint.PPrinter,
- colors: CodeColors,
- command: Int => Seq[String],
- returnType: Class[_],
- argTypes: Class[_]*): Any =
- SourceRuntime.browseObjectMember(
- symbolOwnerCls,
- value,
- memberName,
- pprinter,
- colors,
- n => command(n),
- returnType,
- argTypes: _*
- )
-
- def browseObject(value: Any,
- pprinter: pprint.PPrinter,
- colors: CodeColors,
- command: Int => Seq[String]): Any =
- SourceRuntime.browseObject(
- value,
- pprinter,
- colors,
- n => command(n)
- )
-
- def desugarImpl(s: String)(implicit colors: ammonite.util.CodeColors): Desugared = {
-
- new Desugared(
- ammonite.repl.Highlighter.defaultHighlight(
- s.toVector,
- colors.comment,
- colors.`type`,
- colors.literal,
- colors.keyword,
- fansi.Attr.Reset
- ).mkString
- )
- }
-
-}
-
trait FrontEndAPIImpl extends FrontEndAPI {
+ protected def parser: ammonite.compiler.iface.Parser
def apply(name: String): FrontEnd =
name.toLowerCase(Locale.ROOT) match {
- case "ammonite" => AmmoniteFrontEnd()
- case "windows" => FrontEnds.JLineWindows
- case "unix" => FrontEnds.JLineUnix
+ case "ammonite" => AmmoniteFrontEnd(parser)
+ case "windows" => new FrontEnds.JLineWindows(parser)
+ case "unix" => new FrontEnds.JLineUnix(parser)
case _ => throw new NoSuchElementException(s"Front-end $name")
}
}
diff --git a/amm/repl/src/main/scala/ammonite/repl/FrontEndUtils.scala b/amm/repl/src/main/scala/ammonite/repl/FrontEndUtils.scala
index 129e87060..5688cc0c5 100644
--- a/amm/repl/src/main/scala/ammonite/repl/FrontEndUtils.scala
+++ b/amm/repl/src/main/scala/ammonite/repl/FrontEndUtils.scala
@@ -6,11 +6,11 @@ import ammonite.util.Util.newLine
* Created by haoyi on 8/29/15.
*/
object FrontEndUtils {
- def width =
- if (scala.util.Properties.isWin) ammonite.repl.FrontEnds.JLineWindows.width
+ def width =
+ if (scala.util.Properties.isWin) ammonite.repl.FrontEnds.width
else ammonite.terminal.ConsoleDim.width()
def height =
- if (scala.util.Properties.isWin) ammonite.repl.FrontEnds.JLineWindows.width
+ if (scala.util.Properties.isWin) ammonite.repl.FrontEnds.height
else ammonite.terminal.ConsoleDim.height()
def tabulate(snippetsRaw: Seq[fansi.Str], width: Int): Iterator[String] = {
val gap = 2
diff --git a/amm/repl/src/main/scala/ammonite/repl/FrontEnds.scala b/amm/repl/src/main/scala/ammonite/repl/FrontEnds.scala
index a41f5fc3f..8f34d43a2 100644
--- a/amm/repl/src/main/scala/ammonite/repl/FrontEnds.scala
+++ b/amm/repl/src/main/scala/ammonite/repl/FrontEnds.scala
@@ -5,28 +5,28 @@ import java.io.{InputStream, OutputStream}
import scala.collection.JavaConverters._
import fastparse.Parsed
import fastparse.ParserInput
-import org.jline.reader.{Highlighter => _, _}
+import org.jline.reader._
import org.jline.reader.impl.history.DefaultHistory
import org.jline.terminal._
import org.jline.utils.AttributedString
+import ammonite.compiler.iface.{Parser => IParser}
import ammonite.util.{Catching, Colors, Res}
import ammonite.repl.api.FrontEnd
-import ammonite.interp.{Parsers, Preprocessor}
import org.jline.reader.impl.DefaultParser
object FrontEnds {
- object JLineUnix extends JLineTerm
- object JLineWindows extends JLineTerm
- class JLineTerm() extends FrontEnd {
+ class JLineUnix(codeParser: IParser) extends JLineTerm(codeParser)
+ class JLineWindows(codeParser: IParser) extends JLineTerm(codeParser)
+ class JLineTerm(codeParser: IParser) extends FrontEnd {
private val term = TerminalBuilder.builder().build()
private val readerBuilder = LineReaderBuilder.builder().terminal(term)
- private val ammHighlighter = new AmmHighlighter()
+ private val ammHighlighter = new AmmHighlighter(codeParser)
private val ammCompleter = new AmmCompleter(ammHighlighter)
- private val ammParser = new AmmParser()
+ private val ammParser = new AmmParser(codeParser)
readerBuilder.highlighter(ammHighlighter)
readerBuilder.completer(ammCompleter)
readerBuilder.parser(ammParser)
@@ -79,9 +79,12 @@ object FrontEnds {
} yield res
}
}
+
+ def width = TerminalBuilder.builder().build().getWidth
+ def height = TerminalBuilder.builder().build().getHeight
}
-class AmmCompleter(highlighter: org.jline.reader.Highlighter) extends Completer {
+class AmmCompleter(highlighter: Highlighter) extends Completer {
// completion varies from action to action
var compilerComplete: (Int, String) => (Int, Seq[String], Seq[String]) =
(x, y) => (0, Seq.empty, Seq.empty)
@@ -125,7 +128,7 @@ class AmmCompleter(highlighter: org.jline.reader.Highlighter) extends Completer
}
}
-class AmmParser extends Parser {
+class AmmParser(codeParser: IParser) extends Parser {
class AmmoniteParsedLine(line: String, words: java.util.List[String],
wordIndex: Int, wordCursor: Int, cursor: Int,
val stmts: Seq[String] = Seq.empty // needed for interpreter
@@ -142,9 +145,9 @@ class AmmParser extends Parser {
val words = defParLine.words
val wordIndex = defParLine.wordIndex // index of the current word in the list of words
val wordCursor = defParLine.wordCursor // cursor position within the current word
-
- Parsers.split(line) match {
- case Some(Parsed.Success(stmts, idx)) =>
+
+ codeParser.split(line) match {
+ case Some(Right(stmts)) =>
addHistory(line)
// if ENTER and not at the end of input -> newline
if (context == Parser.ParseContext.ACCEPT_LINE && cursor != line.length) {
@@ -152,14 +155,12 @@ class AmmParser extends Parser {
} else {
new AmmoniteParsedLine(line, words, wordIndex, wordCursor, cursor, stmts)
}
- case Some(f @ Parsed.Failure(p, idx, extra)) =>
+ case Some(Left(err)) =>
// we "accept the failure" only when ENTER is pressed, loops forever otherwise...
// https://groups.google.com/d/msg/jline-users/84fPur0oHKQ/bRnjOJM4BAAJ
if (context == Parser.ParseContext.ACCEPT_LINE) {
addHistory(line)
- throw new SyntaxError(
- Preprocessor.formatFastparseError("(console)", line, f)
- )
+ throw new SyntaxError(err)
} else {
new AmmoniteParsedLine(line, words, wordIndex, wordCursor, cursor)
}
@@ -177,13 +178,13 @@ class AmmParser extends Parser {
class SyntaxError(val msg: String) extends RuntimeException
-class AmmHighlighter extends org.jline.reader.Highlighter {
+class AmmHighlighter(codeParser: IParser) extends Highlighter {
var colors: Colors = Colors.Default
def setErrorIndex(x$1: Int): Unit = ()
def setErrorPattern(x$1: java.util.regex.Pattern): Unit = ()
override def highlight(reader: LineReader, buffer: String): AttributedString = {
- val hl = Highlighter.defaultHighlight(
+ val hl = codeParser.defaultHighlight(
buffer.toVector,
colors.comment(),
colors.`type`(),
diff --git a/amm/repl/src/main/scala/ammonite/repl/Repl.scala b/amm/repl/src/main/scala/ammonite/repl/Repl.scala
index 886a2cb81..e8d6826df 100644
--- a/amm/repl/src/main/scala/ammonite/repl/Repl.scala
+++ b/amm/repl/src/main/scala/ammonite/repl/Repl.scala
@@ -2,12 +2,13 @@ package ammonite.repl
import java.io.{InputStream, InputStreamReader, OutputStream}
-import ammonite.repl.api.{FrontEnd, FrontEndAPI, History, ReplLoad, SourceAPI}
+import ammonite.repl.api.{FrontEnd, History, ReplLoad}
import ammonite.runtime._
import ammonite.terminal.Filter
import ammonite.util.Util.{newLine, normalizeNewlines}
import ammonite.util._
-import ammonite.interp.{CodeWrapper, Interpreter, Parsers, Preprocessor}
+import ammonite.compiler.iface.{CodeWrapper, CompilerBuilder, Parser}
+import ammonite.interp.Interpreter
import coursierapi.Dependency
import scala.annotation.tailrec
@@ -27,6 +28,8 @@ class Repl(input: InputStream,
scriptCodeWrapper: CodeWrapper,
alreadyLoadedDependencies: Seq[Dependency],
importHooks: Map[Seq[String], ImportHook],
+ compilerBuilder: CompilerBuilder,
+ parser: Parser,
initialClassLoader: ClassLoader =
classOf[ammonite.repl.api.ReplAPI].getClassLoader,
classPathWhitelist: Set[Seq[String]]) { repl =>
@@ -35,9 +38,9 @@ class Repl(input: InputStream,
val frontEnd = Ref[FrontEnd](
if (scala.util.Properties.isWin)
- ammonite.repl.FrontEnds.JLineWindows
+ new ammonite.repl.FrontEnds.JLineWindows(parser)
else
- AmmoniteFrontEnd(Filter.empty)
+ AmmoniteFrontEnd(parser, Filter.empty)
)
var lastException: Throwable = null
@@ -71,6 +74,8 @@ class Repl(input: InputStream,
def usedEarlierDefinitions = frames().head.usedEarlierDefinitions
val interp = new Interpreter(
+ compilerBuilder,
+ parser,
printer,
storage,
wd,
@@ -102,8 +107,6 @@ class Repl(input: InputStream,
def fullHistory = storage.fullHistory()
def history = repl.history
def newCompiler() = interp.compilerManager.init(force = true)
- def compiler = interp.compilerManager.compiler.compiler
- def interactiveCompiler = interp.compilerManager.pressy.compiler
def fullImports = repl.fullImports
def imports = repl.imports
def usedEarlierDefinitions = repl.usedEarlierDefinitions
@@ -125,17 +128,16 @@ class Repl(input: InputStream,
apply(normalizeNewlines(os.read(file)))
}
}
+
+ def _compilerManager = interp.compilerManager
}
),
- (
- "ammonite.repl.api.SourceBridge",
- "source",
- new SourceAPIImpl {}
- ),
(
"ammonite.repl.api.FrontEndBridge",
"frontEnd",
- new FrontEndAPIImpl {}
+ new FrontEndAPIImpl {
+ def parser = repl.parser
+ }
)
)
@@ -154,7 +156,7 @@ class Repl(input: InputStream,
// running the user code directly. Could be made longer to better warm more
// code paths, but then the fixed overhead gets larger so not really worth it
val code = s"""val array = Seq.tabulate(10)(_*2).toArray.max"""
- val stmts = Parsers.split(code).get.get.value
+ val stmts = parser.split(code).get.toOption.get
interp.processLine(code, stmts, 9999999, silent = true, () => () /*donothing*/)
}
diff --git a/amm/repl/src/test/scala-2.12/ammonite/unit/SourceTests212.scala b/amm/repl/src/test/scala-2.12/ammonite/unit/SourceTests212.scala
index 88547bed3..d40050427 100644
--- a/amm/repl/src/test/scala-2.12/ammonite/unit/SourceTests212.scala
+++ b/amm/repl/src/test/scala-2.12/ammonite/unit/SourceTests212.scala
@@ -1,17 +1,14 @@
package ammonite.unit
-import ammonite.repl.SourceAPIImpl
-import ammonite.repl.api.{Location, SourceBridge}
import utest._
-import ammonite.repl.tools.source.load
+import ammonite.compiler.tools.source.load
import ammonite.util.Util
+import ammonite.util.Util.Location
//import fastparse.utils.{ElemSetHelper, Generator, IndexedParserInput}
object SourceTests212 extends TestSuite{
val tests = Tests{
- if (SourceBridge.value0 == null)
- SourceBridge.value0 = new SourceAPIImpl {}
def check(loaded: Location, expectedFileName: String, expected: String, slop: Int = 10) = {
diff --git a/amm/repl/src/test/scala/ammonite/DualTestRepl.scala b/amm/repl/src/test/scala/ammonite/DualTestRepl.scala
index 22b481b0e..8726204eb 100644
--- a/amm/repl/src/test/scala/ammonite/DualTestRepl.scala
+++ b/amm/repl/src/test/scala/ammonite/DualTestRepl.scala
@@ -1,8 +1,6 @@
package ammonite
-import ammonite.interp.CodeClassWrapper
-import ammonite.repl.api.SourceBridge
-import ammonite.repl.SourceAPIImpl
+import ammonite.compiler.CodeClassWrapper
import ammonite.util.{Evaluated, Res}
/**
@@ -10,9 +8,6 @@ import ammonite.util.{Evaluated, Res}
*/
class DualTestRepl { dual =>
- if (SourceBridge.value0 == null)
- SourceBridge.value0 = new SourceAPIImpl {}
-
def predef: (String, Option[os.Path]) = ("", None)
val repls = Seq(
diff --git a/amm/repl/src/test/scala/ammonite/TestRepl.scala b/amm/repl/src/test/scala/ammonite/TestRepl.scala
index 7a5c69640..718fc0ed9 100644
--- a/amm/repl/src/test/scala/ammonite/TestRepl.scala
+++ b/amm/repl/src/test/scala/ammonite/TestRepl.scala
@@ -2,7 +2,9 @@ package ammonite
import java.io.PrintStream
-import ammonite.interp.{CodeWrapper, Interpreter, Preprocessor}
+import ammonite.compiler.DefaultCodeWrapper
+import ammonite.compiler.iface.CodeWrapper
+import ammonite.interp.Interpreter
import ammonite.main.Defaults
import ammonite.repl._
import ammonite.repl.api.{FrontEnd, History, ReplLoad}
@@ -20,10 +22,10 @@ import ammonite.runtime.ImportHook
* A test REPL which does not read from stdin or stdout files, but instead lets
* you feed in lines or sessions programmatically and have it execute them.
*/
-class TestRepl {
+class TestRepl { self =>
var allOutput = ""
def predef: (String, Option[os.Path]) = ("", None)
- def codeWrapper: CodeWrapper = CodeWrapper
+ def codeWrapper: CodeWrapper = DefaultCodeWrapper
val tempDir = os.Path(
java.nio.file.Files.createTempDirectory("ammonite-tester")
@@ -61,9 +63,13 @@ class TestRepl {
)
val customPredefs = Seq()
+ val parser = ammonite.compiler.Parsers
+
var currentLine = 0
val interp = try {
new Interpreter(
+ ammonite.compiler.CompilerBuilder,
+ parser,
printer0,
storage = storage,
wd = os.pwd,
@@ -110,8 +116,6 @@ class TestRepl {
def history = new History(Vector())
val colors = Ref(Colors.BlackWhite)
def newCompiler() = interp.compilerManager.init(force = true)
- def compiler = interp.compilerManager.compiler.compiler
- def interactiveCompiler = interp.compilerManager.pressy.compiler
def fullImports = interp.predefImports ++ imports
def imports = frames().head.imports
def usedEarlierDefinitions = frames().head.usedEarlierDefinitions
@@ -153,12 +157,16 @@ class TestRepl {
else
super.print(value, ident, custom)(TPrint.implicitly[T], tcolors, classTagT)
}
+
+ def _compilerManager = interp.compilerManager
}
),
(
"ammonite.repl.api.FrontEndBridge",
"frontEnd",
- new FrontEndAPIImpl {}
+ new FrontEndAPIImpl {
+ def parser = self.parser
+ }
)
)
@@ -215,7 +223,7 @@ class TestRepl {
// ...except for the empty 0-line fragment, and the entire fragment,
// both of which are complete.
for (incomplete <- commandText.inits.toSeq.drop(1).dropRight(1)){
- assert(ammonite.interp.Parsers.split(incomplete.mkString(Util.newLine)).isEmpty)
+ assert(ammonite.compiler.Parsers.split(incomplete.mkString(Util.newLine)).isEmpty)
}
// Finally, actually run the complete command text through the
@@ -320,7 +328,7 @@ class TestRepl {
warningBuffer.clear()
errorBuffer.clear()
infoBuffer.clear()
- val splitted = ammonite.interp.Parsers.split(input).get.get.value
+ val splitted = ammonite.compiler.Parsers.split(input).get.toOption.get
val processed = interp.processLine(
input,
splitted,
diff --git a/amm/repl/src/test/scala/ammonite/TestUtils.scala b/amm/repl/src/test/scala/ammonite/TestUtils.scala
index fef31cd76..5ac954a55 100644
--- a/amm/repl/src/test/scala/ammonite/TestUtils.scala
+++ b/amm/repl/src/test/scala/ammonite/TestUtils.scala
@@ -2,7 +2,8 @@ package ammonite
import java.io.PrintStream
-import ammonite.interp.{CodeWrapper, Interpreter, Preprocessor}
+import ammonite.compiler.DefaultCodeWrapper
+import ammonite.interp.Interpreter
import ammonite.main.Defaults
import ammonite.runtime.{Frame, Storage}
import ammonite.util._
@@ -23,6 +24,8 @@ object TestUtils {
val startFrame = Frame.createInitial(initialClassLoader)
val printStream = new PrintStream(System.out)
val interp = new Interpreter(
+ ammonite.compiler.CompilerBuilder,
+ ammonite.compiler.Parsers,
printer = Printer(
printStream, new PrintStream(System.err), printStream,
@@ -34,8 +37,8 @@ object TestUtils {
getFrame = () => startFrame,
createFrame = () => throw new Exception("unsupported"),
initialClassLoader = initialClassLoader,
- replCodeWrapper = CodeWrapper,
- scriptCodeWrapper = CodeWrapper,
+ replCodeWrapper = DefaultCodeWrapper,
+ scriptCodeWrapper = DefaultCodeWrapper,
alreadyLoadedDependencies = Defaults.alreadyLoadedDependencies("amm-test-dependencies.txt"),
importHooks = ImportHook.defaults,
classPathWhitelist = ammonite.repl.Repl.getClassPathWhitelist(thin = true)
diff --git a/amm/repl/src/test/scala/ammonite/session/AdvancedTests.scala b/amm/repl/src/test/scala/ammonite/session/AdvancedTests.scala
index e02b16f9a..0de5a9f81 100644
--- a/amm/repl/src/test/scala/ammonite/session/AdvancedTests.scala
+++ b/amm/repl/src/test/scala/ammonite/session/AdvancedTests.scala
@@ -2,7 +2,7 @@ package ammonite.session
import ammonite.TestUtils._
import ammonite.DualTestRepl
-import ammonite.util.{Res, Util}
+import ammonite.util.Res
import utest._
@@ -373,7 +373,7 @@ object AdvancedTests extends TestSuite{
test("desugar"){
check.session("""
@ desugar{1 + 2 max 3}
- res0: ammonite.repl.tools.Desugared = scala.Predef.intWrapper(3).max(3)
+ res0: compiler.tools.Desugared = scala.Predef.intWrapper(3).max(3)
""")
}
test("loadingModulesInPredef"){
diff --git a/amm/repl/src/test/scala/ammonite/session/SerializationTests.scala b/amm/repl/src/test/scala/ammonite/session/SerializationTests.scala
index 22f75d459..7cb544bb0 100644
--- a/amm/repl/src/test/scala/ammonite/session/SerializationTests.scala
+++ b/amm/repl/src/test/scala/ammonite/session/SerializationTests.scala
@@ -1,7 +1,7 @@
package ammonite.session
import ammonite.TestRepl
-import ammonite.interp.CodeClassWrapper
+import ammonite.compiler.CodeClassWrapper
import utest._
object SerializationTests extends TestSuite{
diff --git a/amm/repl/src/test/scala/ammonite/unit/HighlightTests.scala b/amm/repl/src/test/scala/ammonite/unit/HighlightTests.scala
index 125abc8de..5d9b6d131 100644
--- a/amm/repl/src/test/scala/ammonite/unit/HighlightTests.scala
+++ b/amm/repl/src/test/scala/ammonite/unit/HighlightTests.scala
@@ -1,11 +1,11 @@
package ammonite.unit
-import ammonite.repl.Highlighter
+import ammonite.compiler.Parsers
import utest._
object HighlightTests extends TestSuite{
- def testHighlight(buffer: Vector[Char]) = Highlighter.defaultHighlight(
+ def testHighlight(buffer: Vector[Char]) = Parsers.defaultHighlight(
buffer,
fansi.Color.Blue,
fansi.Color.Green,
@@ -33,7 +33,7 @@ object HighlightTests extends TestSuite{
val paths = os.walk(os.pwd).filter(_.ext == "scala")
for(path <- paths){
val code = os.read(path)
- val out = Highlighter.defaultHighlight(
+ val out = Parsers.defaultHighlight(
code.toVector,
fansi.Underlined.On,
fansi.Underlined.On,
diff --git a/amm/repl/src/test/scala/ammonite/unit/ParserTests.scala b/amm/repl/src/test/scala/ammonite/unit/ParserTests.scala
index 02c3a6b79..e3adcfe4c 100644
--- a/amm/repl/src/test/scala/ammonite/unit/ParserTests.scala
+++ b/amm/repl/src/test/scala/ammonite/unit/ParserTests.scala
@@ -1,6 +1,5 @@
package ammonite.unit
-import ammonite.repl.Highlighter
import ammonite.util.Util
import utest._
@@ -68,14 +67,14 @@ object ParserTests extends TestSuite{
// Not nearly comprehensive, but hopefully if someone really borks this
// somewhat-subtle logic around the parsers, one of these will catch it
test("endOfCommandDetection"){
- def assertResult(x: String, pred: Option[fastparse.Parsed[_]] => Boolean) = {
- val res = ammonite.interp.Parsers.split(x)
+ def assertResult(x: String, pred: Option[Either[String, _]] => Boolean) = {
+ val res = ammonite.compiler.Parsers.split(x)
assert(pred(res))
}
def assertIncomplete(x: String) = assertResult(x, _.isEmpty)
def assertComplete(x: String) = assertResult(x, _.isDefined)
def assertInvalid(x: String) =
- assertResult(x, res => res.isDefined && res.get.isInstanceOf[fastparse.Parsed.Failure])
+ assertResult(x, res => res.isDefined && res.get.isLeft)
test("endOfCommand"){
test - assertComplete("{}")
diff --git a/amm/repl/src/test/scala/ammonite/unit/SourceTests.scala b/amm/repl/src/test/scala/ammonite/unit/SourceTests.scala
index 9f72626e2..be6df8e3e 100644
--- a/amm/repl/src/test/scala/ammonite/unit/SourceTests.scala
+++ b/amm/repl/src/test/scala/ammonite/unit/SourceTests.scala
@@ -1,19 +1,15 @@
package ammonite.unit
-import ammonite.repl.SourceAPIImpl
-import ammonite.repl.api.{Location, SourceBridge}
import utest._
-import ammonite.repl.tools.source.load
+import ammonite.compiler.tools.source.load
import ammonite.util.Util
+import ammonite.util.Util.Location
import java.io.InputStream
object SourceTests extends TestSuite{
val tests = Tests{
- if (SourceBridge.value0 == null)
- SourceBridge.value0 = new SourceAPIImpl {}
-
def check(loaded: Location, expectedFileName: String, expected: String, slop: Int = 10) = {
diff --git a/amm/runtime/src/main/scala/ammonite/runtime/ClassLoaders.scala b/amm/runtime/src/main/scala/ammonite/runtime/ClassLoaders.scala
index da2bb5fb6..c9110af25 100644
--- a/amm/runtime/src/main/scala/ammonite/runtime/ClassLoaders.scala
+++ b/amm/runtime/src/main/scala/ammonite/runtime/ClassLoaders.scala
@@ -25,7 +25,7 @@ class Frame(val classloader: SpecialClassLoader,
val pluginClassloader: SpecialClassLoader,
private[this] var imports0: Imports,
private[this] var classpath0: Seq[java.net.URL],
- private[this] var usedEarlierDefinitions0: Seq[String]) extends ammonite.repl.api.Frame{
+ private[this] var usedEarlierDefinitions0: Seq[String]) extends ammonite.util.Frame{
private var frozen0 = false
def frozen = frozen0
def freeze(): Unit = {
@@ -204,7 +204,7 @@ class SpecialClassLoader(parent: ClassLoader,
parentSignature: Seq[(Either[String, java.net.URL], Long)],
var specialLocalClasses: Set[String],
urls: URL*)
- extends ammonite.repl.api.ReplClassLoader(urls.toArray, parent){
+ extends ammonite.util.ReplClassLoader(urls.toArray, parent){
/**
* Files which have been compiled, stored so that our special
diff --git a/amm/runtime/src/main/scala/ammonite/runtime/Evaluator.scala b/amm/runtime/src/main/scala/ammonite/runtime/Evaluator.scala
index c62e4c31c..d9ad16183 100644
--- a/amm/runtime/src/main/scala/ammonite/runtime/Evaluator.scala
+++ b/amm/runtime/src/main/scala/ammonite/runtime/Evaluator.scala
@@ -5,7 +5,7 @@ import java.lang.reflect.InvocationTargetException
import ammonite._
import ammonite.interp.api.AmmoniteExit
import util.Util.{ClassFiles, newLine}
-import ammonite.util._
+import ammonite.util.{Frame => _, _}
import scala.util.Try
diff --git a/amm/runtime/src/main/scala/ammonite/runtime/Storage.scala b/amm/runtime/src/main/scala/ammonite/runtime/Storage.scala
index c200e45b9..a63165fa8 100644
--- a/amm/runtime/src/main/scala/ammonite/runtime/Storage.scala
+++ b/amm/runtime/src/main/scala/ammonite/runtime/Storage.scala
@@ -31,6 +31,8 @@ trait Storage{
tag: Tag): Unit
def classFilesListLoad(filePathPrefix: os.SubPath, tag: Tag): Option[ScriptOutput]
def getSessionId: Long
+
+ def dirOpt: Option[os.Path] = None
}
object Storage{
@@ -339,5 +341,7 @@ object Storage{
try Some((os.read(predef), predef))
catch { case e: java.nio.file.NoSuchFileException => Some(("", predef))}
}
+
+ override def dirOpt: Option[os.Path] = Some(dir)
}
}
diff --git a/amm/src/main/scala/ammonite/Main.scala b/amm/src/main/scala/ammonite/Main.scala
index ea8cec587..a9867b030 100644
--- a/amm/src/main/scala/ammonite/Main.scala
+++ b/amm/src/main/scala/ammonite/Main.scala
@@ -4,11 +4,13 @@ import java.io.{InputStream, OutputStream, PrintStream}
import java.net.URLClassLoader
import java.nio.file.NoSuchFileException
-import ammonite.interp.{Watchable, CodeClassWrapper, CodeWrapper, Interpreter, PredefInitialization}
+import ammonite.compiler.{CodeClassWrapper, DefaultCodeWrapper}
+import ammonite.compiler.iface.{CodeWrapper, CompilerBuilder, Parser}
+import ammonite.interp.{Watchable, Interpreter, PredefInitialization}
import ammonite.interp.script.AmmoniteBuildServer
import ammonite.runtime.{Frame, Storage}
import ammonite.main._
-import ammonite.repl.{FrontEndAPIImpl, Repl, SourceAPIImpl}
+import ammonite.repl.{FrontEndAPIImpl, Repl}
import ammonite.util.Util.newLine
import ammonite.util._
@@ -69,11 +71,14 @@ case class Main(predefCode: String = "",
verboseOutput: Boolean = true,
remoteLogging: Boolean = true,
colors: Colors = Colors.Default,
- replCodeWrapper: CodeWrapper = CodeWrapper,
- scriptCodeWrapper: CodeWrapper = CodeWrapper,
+ replCodeWrapper: CodeWrapper = DefaultCodeWrapper,
+ scriptCodeWrapper: CodeWrapper = DefaultCodeWrapper,
alreadyLoadedDependencies: Seq[Dependency] =
Defaults.alreadyLoadedDependencies(),
importHooks: Map[Seq[String], ImportHook] = ImportHook.defaults,
+ compilerBuilder: CompilerBuilder = ammonite.compiler.CompilerBuilder,
+ // by-name, so that fastparse isn't loaded when we don't need it
+ parser: () => Parser = () => ammonite.compiler.Parsers,
classPathWhitelist: Set[Seq[String]] = Set.empty){
def loadedPredefFile = predefFile match{
@@ -135,6 +140,8 @@ case class Main(predefCode: String = "",
scriptCodeWrapper = scriptCodeWrapper,
alreadyLoadedDependencies = alreadyLoadedDependencies,
importHooks = importHooks,
+ compilerBuilder = compilerBuilder,
+ parser = parser(),
initialClassLoader = initialClassLoader,
classPathWhitelist = classPathWhitelist
)
@@ -159,7 +166,10 @@ case class Main(predefCode: String = "",
val customPredefs = predefFileInfoOpt.toSeq ++ Seq(
PredefInfo(Name("CodePredef"), predefCode, false, None)
)
+ lazy val parser0 = parser()
val interp = new Interpreter(
+ ammonite.compiler.CompilerBuilder,
+ parser0,
printer,
storageBackend,
wd,
@@ -175,15 +185,12 @@ case class Main(predefCode: String = "",
classPathWhitelist = classPathWhitelist
)
val bridges = Seq(
- (
- "ammonite.repl.api.SourceBridge",
- "source",
- new SourceAPIImpl {}
- ),
(
"ammonite.repl.api.FrontEndBridge",
"frontEnd",
- new FrontEndAPIImpl {}
+ new FrontEndAPIImpl {
+ def parser = parser0
+ }
)
)
interp.initializePredef(Seq(), customPredefs, bridges, augmentedImports) match{
@@ -294,6 +301,9 @@ object Main{
case Right(cliConfig) =>
if (cliConfig.core.bsp.value) {
val buildServer = new AmmoniteBuildServer(
+ ???,
+ ammonite.compiler.Parsers,
+ ammonite.compiler.DefaultCodeWrapper,
initialScripts = cliConfig.rest.map(os.Path(_)),
initialImports = PredefInitialization.initBridges(
Seq("ammonite.interp.api.InterpBridge" -> "interp")
@@ -452,9 +462,10 @@ class MainRunner(cliConfig: Config,
new Storage.Folder(cliConfig.core.home, isRepl)
}
+ lazy val parser = ammonite.compiler.Parsers
val codeWrapper =
if (cliConfig.repl.classBased.value) CodeClassWrapper
- else CodeWrapper
+ else DefaultCodeWrapper
Main(
cliConfig.predef.predefCode,
@@ -471,6 +482,7 @@ class MainRunner(cliConfig: Config,
colors = colors,
replCodeWrapper = codeWrapper,
scriptCodeWrapper = codeWrapper,
+ parser = () => parser,
alreadyLoadedDependencies =
Defaults.alreadyLoadedDependencies(),
classPathWhitelist = ammonite.repl.Repl.getClassPathWhitelist(cliConfig.core.thin.value)
diff --git a/amm/src/main/scala/ammonite/main/Scripts.scala b/amm/src/main/scala/ammonite/main/Scripts.scala
index 803fdfb6f..9a5cc6303 100644
--- a/amm/src/main/scala/ammonite/main/Scripts.scala
+++ b/amm/src/main/scala/ammonite/main/Scripts.scala
@@ -50,7 +50,7 @@ object Scripts {
}
scriptMains = interp.scriptCodeWrapper match{
- case ammonite.interp.CodeWrapper =>
+ case ammonite.compiler.DefaultCodeWrapper =>
Some(
interp
.evalClassloader
@@ -61,7 +61,7 @@ object Scripts {
.apply()
)
- case ammonite.interp.CodeClassWrapper =>
+ case ammonite.compiler.CodeClassWrapper =>
val outer = interp
.evalClassloader
.loadClass(routeClsName)
diff --git a/amm/src/test/scala/ammonite/interp/CachingTests.scala b/amm/src/test/scala/ammonite/interp/CachingTests.scala
index 4f8faa182..85dfddeb7 100644
--- a/amm/src/test/scala/ammonite/interp/CachingTests.scala
+++ b/amm/src/test/scala/ammonite/interp/CachingTests.scala
@@ -159,8 +159,14 @@ object CachingTests extends TestSuite{
java.nio.file.Files.createTempDirectory("ammonite-tester-x")
)
- val interp1 = createTestInterp(new Storage.Folder(tempDir))
- val interp2 = createTestInterp(new Storage.Folder(tempDir))
+ val interp1 = createTestInterp(
+ new Storage.Folder(tempDir),
+ predefImports = Interpreter.predefImports
+ )
+ val interp2 = createTestInterp(
+ new Storage.Folder(tempDir),
+ predefImports = Interpreter.predefImports
+ )
runScript(os.pwd, scriptPath/"cachedCompilerInit.sc", interp1)
runScript(os.pwd, scriptPath/"cachedCompilerInit.sc", interp2)
@@ -182,7 +188,7 @@ object CachingTests extends TestSuite{
""")
val scriptFile = os.temp("""div("<('.'<)", y).render""")
- def processAndCheckCompiler(f: ammonite.interp.Compiler => Boolean) ={
+ def processAndCheckCompiler(f: ammonite.compiler.iface.Compiler => Boolean) ={
val interp = createTestInterp(
new Storage.Folder(tempDir){
override val predef = predefFile
diff --git a/amm/src/test/scala/ammonite/interp/CompilerSettingsTests.scala b/amm/src/test/scala/ammonite/interp/CompilerSettingsTests.scala
index b07fd0396..00e8b2baf 100644
--- a/amm/src/test/scala/ammonite/interp/CompilerSettingsTests.scala
+++ b/amm/src/test/scala/ammonite/interp/CompilerSettingsTests.scala
@@ -23,7 +23,14 @@ object CompilerSettingsTests extends TestSuite {
val interp = createTestInterp(storage)
Scripts.runScript(os.pwd, scriptPath / "configureCompiler.sc", interp)
- assert(interp.compilerManager.compiler.compiler.useOffsetPositions)
+ assert(
+ interp
+ .compilerManager
+ .asInstanceOf[ammonite.compiler.CompilerLifecycleManager]
+ .compiler
+ .compiler
+ .useOffsetPositions
+ )
}
}
@@ -32,10 +39,20 @@ object CompilerSettingsTests extends TestSuite {
// which is called BEFORE the compiler instantiates, resulting in
// useOffsetPositions initializing as false, as expected
val storage = Storage.InMemory()
- val interp = createTestInterp(storage)
+ val interp = createTestInterp(
+ storage,
+ predefImports = Interpreter.predefImports
+ )
Scripts.runScript(os.pwd, scriptPath / "preConfigureCompiler.sc", interp)
- assert(!interp.compilerManager.compiler.compiler.useOffsetPositions)
+ assert(
+ !interp
+ .compilerManager
+ .asInstanceOf[ammonite.compiler.CompilerLifecycleManager]
+ .compiler
+ .compiler
+ .useOffsetPositions
+ )
}
}
}
diff --git a/amm/src/test/scala/ammonite/interp/YRangeposTests.scala b/amm/src/test/scala/ammonite/interp/YRangeposTests.scala
index 93c781e40..9e0cc3a51 100644
--- a/amm/src/test/scala/ammonite/interp/YRangeposTests.scala
+++ b/amm/src/test/scala/ammonite/interp/YRangeposTests.scala
@@ -6,8 +6,6 @@ import ammonite.runtime.Storage
import ammonite.main._
import utest._
-import scala.tools.nsc.Global
-
object YRangeposTests extends TestSuite {
val tests = Tests {
println("YRangeposTests")
@@ -25,7 +23,10 @@ object YRangeposTests extends TestSuite {
// This tests shows that enabling Yrangepos does not mess with ammonite's
// behaviour. The compiler not crashing is the test itself.
val storage = Storage.InMemory()
- val interp = createTestInterp(storage)
+ val interp = createTestInterp(
+ storage,
+ predefImports = Interpreter.predefImports
+ )
val res = Scripts.runScript(os.pwd, scriptFolderPath / "yRangepos.sc", interp)
assert(res.isSuccess)
}
diff --git a/amm/src/test/scala/ammonite/interp/script/AmmoniteBuildServerTests.scala b/amm/src/test/scala/ammonite/interp/script/AmmoniteBuildServerTests.scala
index d8b540874..429bc830f 100644
--- a/amm/src/test/scala/ammonite/interp/script/AmmoniteBuildServerTests.scala
+++ b/amm/src/test/scala/ammonite/interp/script/AmmoniteBuildServerTests.scala
@@ -670,7 +670,12 @@ object AmmoniteBuildServerTests extends TestSuite {
def this(script: os.Path*) =
this(wd, script)
- val server = new AmmoniteBuildServer(initialScripts = script)
+ val server = new AmmoniteBuildServer(
+ ammonite.compiler.CompilerBuilder,
+ ammonite.compiler.Parsers,
+ ammonite.compiler.DefaultCodeWrapper,
+ initialScripts = script
+ )
val client = new TestBuildClient
server.onConnectWithClient(client)
diff --git a/amm/runtime/src/main/java/io/github/retronym/java9rtexport/Copy.java b/amm/util/src/main/java/io/github/retronym/java9rtexport/Copy.java
similarity index 100%
rename from amm/runtime/src/main/java/io/github/retronym/java9rtexport/Copy.java
rename to amm/util/src/main/java/io/github/retronym/java9rtexport/Copy.java
diff --git a/amm/runtime/src/main/java/io/github/retronym/java9rtexport/Export.java b/amm/util/src/main/java/io/github/retronym/java9rtexport/Export.java
similarity index 100%
rename from amm/runtime/src/main/java/io/github/retronym/java9rtexport/Export.java
rename to amm/util/src/main/java/io/github/retronym/java9rtexport/Export.java
diff --git a/amm/runtime/src/main/scala/ammonite/runtime/Classpath.scala b/amm/util/src/main/scala/ammonite/util/Classpath.scala
similarity index 83%
rename from amm/runtime/src/main/scala/ammonite/runtime/Classpath.scala
rename to amm/util/src/main/scala/ammonite/util/Classpath.scala
index 71c85f8f1..31ef8d239 100644
--- a/amm/runtime/src/main/scala/ammonite/runtime/Classpath.scala
+++ b/amm/util/src/main/scala/ammonite/util/Classpath.scala
@@ -1,7 +1,8 @@
-package ammonite.runtime
+package ammonite.util
import java.io.File
import java.net.URL
+import java.nio.file.{Path, Paths}
import java.util.zip.{ZipFile, ZipInputStream}
@@ -25,16 +26,14 @@ object Classpath {
* memory but is better than reaching all over the filesystem every time we
* want to do something.
*/
- def classpath(classLoader: ClassLoader, storage: Storage): Vector[URL] = {
- def rtCacheDir(storage: Storage): Option[os.Path] = storage match {
- case storage: Storage.Folder =>
- // no need to cache if the storage is in tmpdir
- // because it is temporary
- if (storage.dir.wrapped.startsWith(
- java.nio.file.Paths.get(System.getProperty("java.io.tmpdir"))))
- None
- else Some(storage.dir)
- case _ => None
+ def classpath(
+ classLoader: ClassLoader,
+ rtCacheDir: Option[Path]
+ ): Vector[URL] = {
+ lazy val actualRTCacheDir = rtCacheDir.filter { dir =>
+ // no need to cache if the storage is in tmpdir
+ // because it is temporary
+ !dir.startsWith(Paths.get(System.getProperty("java.io.tmpdir")))
}
var current = classLoader
@@ -73,8 +72,8 @@ object Classpath {
.loadClass("javax.script.ScriptEngineManager")
} catch {
case _: ClassNotFoundException =>
- rtCacheDir(storage) match {
- case Some(path) => files.append(Export.rtAt(path.toIO).toURI.toURL)
+ actualRTCacheDir match {
+ case Some(path) => files.append(Export.rtAt(path.toFile).toURI.toURL)
case _ => files.append(Export.rt().toURI.toURL)
}
}
diff --git a/amm/repl/api/src/main/scala/ammonite/repl/api/Frame.scala b/amm/util/src/main/scala/ammonite/util/Frame.scala
similarity index 77%
rename from amm/repl/api/src/main/scala/ammonite/repl/api/Frame.scala
rename to amm/util/src/main/scala/ammonite/util/Frame.scala
index 79f733be7..8d0a8af46 100644
--- a/amm/repl/api/src/main/scala/ammonite/repl/api/Frame.scala
+++ b/amm/util/src/main/scala/ammonite/util/Frame.scala
@@ -1,4 +1,4 @@
-package ammonite.repl.api
+package ammonite.util
import java.net.URL
@@ -7,4 +7,5 @@ trait Frame {
def pluginClassloader: ReplClassLoader
def classpath: Seq[URL]
+ def version: Int
}
diff --git a/amm/repl/api/src/main/scala/ammonite/repl/api/ReplClassLoader.scala b/amm/util/src/main/scala/ammonite/util/ReplClassLoader.scala
similarity index 88%
rename from amm/repl/api/src/main/scala/ammonite/repl/api/ReplClassLoader.scala
rename to amm/util/src/main/scala/ammonite/util/ReplClassLoader.scala
index 08e5d76c7..9db1d33c8 100644
--- a/amm/repl/api/src/main/scala/ammonite/repl/api/ReplClassLoader.scala
+++ b/amm/util/src/main/scala/ammonite/util/ReplClassLoader.scala
@@ -1,4 +1,4 @@
-package ammonite.repl.api
+package ammonite.util
import java.net.{URL, URLClassLoader}
diff --git a/amm/util/src/main/scala/ammonite/util/Util.scala b/amm/util/src/main/scala/ammonite/util/Util.scala
index 8321c9236..d353f9f31 100644
--- a/amm/util/src/main/scala/ammonite/util/Util.scala
+++ b/amm/util/src/main/scala/ammonite/util/Util.scala
@@ -126,4 +126,6 @@ object Util{
transpose(xs, Nil).reverse
}
-}
\ No newline at end of file
+
+ case class Location(fileName: String, lineNum: Int, fileContent: String)
+}
diff --git a/build.sc b/build.sc
index aa9a0acd5..70809c978 100644
--- a/build.sc
+++ b/build.sc
@@ -103,12 +103,6 @@ trait AmmModule extends AmmInternalModule with PublishModule{
)
)
- def transitiveSources: T[Seq[PathRef]] = T{
- mill.define.Task.traverse(this +: moduleDeps)(m =>
- T.task{m.sources()}
- )().flatten
- }
-
def transitiveJars: T[Agg[PathRef]] = T{
mill.define.Task.traverse(this +: moduleDeps)(m =>
T.task{m.jar()}
@@ -123,7 +117,6 @@ trait AmmModule extends AmmInternalModule with PublishModule{
}
trait AmmDependenciesResourceFileModule extends JavaModule{
- def crossScalaVersion: String
def dependencyResourceFileName: String
def dependencyFileResources = T{
val deps0 = T.task{compileIvyDeps() ++ transitiveIvyDeps()}()
@@ -136,7 +129,6 @@ trait AmmDependenciesResourceFileModule extends JavaModule{
Seq(PathRef(generateDependenciesFile(
- crossScalaVersion,
dependencyResourceFileName,
res.minDependencies.toSeq
)))
@@ -194,29 +186,59 @@ object amm extends Cross[MainModule](fullCrossScalaVersions:_*){
)
}
+ object compiler extends Cross[CompilerModule](fullCrossScalaVersions:_*) {
+ object interface extends Cross[CompilerInterfaceModule](fullCrossScalaVersions:_*)
+ class CompilerInterfaceModule(val crossScalaVersion: String) extends AmmModule{
+ def artifactName = "ammonite-compiler-interface"
+ def moduleDeps = Seq(amm.util())
+ def exposedClassPath = T{
+ runClasspath() ++
+ externalSources() ++
+ transitiveJars() ++
+ transitiveSourceJars()
+ }
+ }
+ }
+ class CompilerModule(val crossScalaVersion: String) extends AmmModule{
+ def moduleDeps = Seq(amm.compiler.interface(), amm.util(), amm.repl.api())
+ def crossFullScalaVersion = true
+ def ivyDeps = T {
+ Agg(
+ ivy"org.scala-lang:scala-compiler:${scalaVersion()}",
+ ivy"com.lihaoyi::scalaparse:2.3.0",
+ ivy"org.scala-lang.modules::scala-xml:2.0.0-M3",
+ ivy"org.javassist:javassist:3.21.0-GA",
+ ivy"com.github.javaparser:javaparser-core:3.2.5"
+ )
+ }
+
+ def exposedClassPath = T{
+ runClasspath() ++
+ externalSources() ++
+ transitiveJars() ++
+ transitiveSourceJars()
+ }
+ }
+
object interp extends Cross[InterpModule](fullCrossScalaVersions:_*){
object api extends Cross[InterpApiModule](fullCrossScalaVersions:_*)
class InterpApiModule(val crossScalaVersion: String) extends AmmModule with AmmDependenciesResourceFileModule{
- def moduleDeps = Seq(ops(), amm.util())
+ def moduleDeps = Seq(amm.compiler.interface(), ops(), amm.util())
def crossFullScalaVersion = true
def dependencyResourceFileName = "amm-interp-api-dependencies.txt"
def ivyDeps = Agg(
- ivy"org.scala-lang:scala-compiler:$crossScalaVersion",
ivy"org.scala-lang:scala-reflect:$crossScalaVersion",
ivy"io.get-coursier:interface:0.0.21"
)
}
}
class InterpModule(val crossScalaVersion: String) extends AmmModule{
- def moduleDeps = Seq(ops(), amm.util(), amm.runtime())
+ def moduleDeps = Seq(ops(), amm.util(), amm.runtime(), amm.compiler.interface())
def crossFullScalaVersion = true
def ivyDeps = Agg(
ivy"ch.epfl.scala:bsp4j:$bspVersion",
ivy"org.scalameta::trees:4.4.6",
- ivy"org.scala-lang:scala-compiler:$crossScalaVersion",
ivy"org.scala-lang:scala-reflect:$crossScalaVersion",
- ivy"com.lihaoyi::scalaparse:2.3.0",
- ivy"org.javassist:javassist:3.21.0-GA",
ivy"org.scala-lang.modules::scala-xml:1.2.0"
)
}
@@ -260,13 +282,13 @@ object amm extends Cross[MainModule](fullCrossScalaVersions:_*){
def moduleDeps = Seq(
ops(), amm.util(),
amm.runtime(), amm.interp(),
- terminal()
+ terminal(),
+ amm.compiler.interface()
)
def ivyDeps = Agg(
ivy"org.jline:jline-terminal:3.14.1",
ivy"org.jline:jline-terminal-jna:3.14.1",
ivy"org.jline:jline-reader:3.14.1",
- ivy"com.github.javaparser:javaparser-core:3.2.5",
// ivy"com.github.scopt::scopt:3.7.1"
)
@@ -274,10 +296,12 @@ object amm extends Cross[MainModule](fullCrossScalaVersions:_*){
def crossScalaVersion = ReplModule.this.crossScalaVersion
def scalaVersion = ReplModule.this.crossScalaVersion
def dependencyResourceFileName = "amm-test-dependencies.txt"
+ def moduleDeps = super.moduleDeps ++ Seq(amm.compiler())
def thinWhitelist = T{
generateApiWhitelist(
amm.repl.api().exposedClassPath() ++
+ amm.compiler().exposedClassPath() ++
Seq(compile().classes) ++
resolveDeps(T.task{compileIvyDeps() ++ transitiveIvyDeps()})()
)
@@ -293,7 +317,7 @@ object amm extends Cross[MainModule](fullCrossScalaVersions:_*){
ReplModule.this.externalSources() ++
resolveDeps(ivyDeps, sources = true)()).distinct
}
- def ivyDeps = super.ivyDeps() ++ Agg(
+ def ivyDeps = super.ivyDeps() ++ amm.compiler().ivyDeps() ++ Agg(
ivy"org.scalaz::scalaz-core:7.2.27"
)
}
@@ -314,7 +338,8 @@ class MainModule(val crossScalaVersion: String)
amm.util(), amm.runtime(),
amm.interp.api(),
amm.repl.api(),
- amm.interp(), amm.repl()
+ amm.interp(), amm.repl(),
+ amm.compiler()
)
def runClasspath =
@@ -342,7 +367,8 @@ class MainModule(val crossScalaVersion: String)
def thinWhitelist = T{
generateApiWhitelist(
- amm.repl.api().exposedClassPath()
+ amm.repl.api().exposedClassPath() ++
+ amm.compiler().exposedClassPath()
)
}
def localClasspath = T{
@@ -394,6 +420,7 @@ class MainModule(val crossScalaVersion: String)
def thinWhitelist = T{
generateApiWhitelist(
amm.repl.api().exposedClassPath() ++
+ amm.compiler().exposedClassPath() ++
Seq(amm.repl().test.compile().classes, compile().classes) ++
resolveDeps(T.task{compileIvyDeps() ++ transitiveIvyDeps()})()
)
@@ -455,6 +482,7 @@ class ShellModule(val crossScalaVersion: String) extends AmmModule{
def thinWhitelist = T{
generateApiWhitelist(
amm.repl.api().exposedClassPath() ++
+ amm.compiler().exposedClassPath() ++
Seq(amm.repl().test.compile().classes, compile().classes) ++
resolveDeps(T.task{compileIvyDeps() ++ transitiveIvyDeps()})()
)
@@ -548,8 +576,7 @@ def generateConstantsFile(version: String = buildVersion,
ctx.dest/"Constants.scala"
}
-def generateDependenciesFile(scalaVersion: String,
- fileName: String,
+def generateDependenciesFile(fileName: String,
deps: Seq[coursier.Dependency])
(implicit ctx: mill.util.Ctx.Dest) = {
diff --git a/shell/src/main/scala/ammonite/shell/Configure.scala b/shell/src/main/scala/ammonite/shell/Configure.scala
index e2c3f5b43..446d26625 100644
--- a/shell/src/main/scala/ammonite/shell/Configure.scala
+++ b/shell/src/main/scala/ammonite/shell/Configure.scala
@@ -10,10 +10,11 @@ object Configure {
def apply(interp: InterpAPI, repl: ReplAPI, wd: => ammonite.ops.Path) = {
if (scala.util.Properties.isWin) {
- repl.frontEnd() = ammonite.repl.FrontEnds.JLineWindows
+ repl.frontEnd() = new ammonite.repl.FrontEnds.JLineWindows(ammonite.compiler.Parsers)
interp.colors() = ammonite.util.Colors.BlackWhite
} else {
repl.frontEnd() = ammonite.repl.AmmoniteFrontEnd(
+ ammonite.compiler.Parsers,
ammonite.shell.PathComplete.pathCompleteFilter(wd, interp.colors())
)
}
diff --git a/shell/src/main/scala/ammonite/shell/PathComplete.scala b/shell/src/main/scala/ammonite/shell/PathComplete.scala
index 33583dad1..5ee0735c5 100644
--- a/shell/src/main/scala/ammonite/shell/PathComplete.scala
+++ b/shell/src/main/scala/ammonite/shell/PathComplete.scala
@@ -4,9 +4,9 @@ import java.io.OutputStreamWriter
import ammonite.terminal._
import Filter._
-import ammonite.repl.{FrontEndUtils, Highlighter}
+import ammonite.repl.FrontEndUtils
import ammonite.util.Colors
-import ammonite.interp.Parsers
+import ammonite.compiler.{Highlighter, Parsers}
import ammonite.terminal._
import ammonite.terminal.LazyList.~:
/**