Skip to content

Commit

Permalink
Add compiler-interface module
Browse files Browse the repository at this point in the history
  • Loading branch information
alexarchambault committed Jan 14, 2021
1 parent bf2034f commit b9ac9af
Show file tree
Hide file tree
Showing 7 changed files with 258 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
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)
}
Original file line number Diff line number Diff line change
@@ -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]]
)

}

Original file line number Diff line number Diff line change
@@ -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
)

}
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
@@ -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)
}
Original file line number Diff line number Diff line change
@@ -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
)

}
14 changes: 13 additions & 1 deletion build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,19 @@ object amm extends Cross[MainModule](fullCrossScalaVersions:_*){
)
}

object compiler extends Cross[CompilerModule](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.util(), amm.repl.api())
def crossFullScalaVersion = true
Expand Down

0 comments on commit b9ac9af

Please sign in to comment.