Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
use type-based instead of annotation-based approach; reorganized and …
…simplified codebase a little
- Loading branch information
1 parent
98e0020
commit a9c1bfd
Showing
14 changed files
with
195 additions
and
200 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
package dslparadise | ||
|
||
import scala.tools.nsc.{Global, SubComponent} | ||
import scala.tools.nsc.plugins.{Plugin => NscPlugin} | ||
import scala.collection.mutable.{Map, Set} | ||
import dslparadise.typechecker.Analyzer | ||
|
||
class Plugin(val global: Global) extends NscPlugin { | ||
import global._ | ||
|
||
val name = "dslparadise" | ||
val description = "DSL Paradise" | ||
val components = List.empty | ||
|
||
// The initialization process (i.e. rewiring phase factories to modify typer | ||
// functionality) is based on "Macro Paradise" code from the Scala 2.10 branch. | ||
// Scala 2.11 introduced new complier plugin capabilities which simplified | ||
// "Macro Paradise" code but cannot be employed for our use case. | ||
|
||
val phasesDescMap = classOf[Global] | ||
.getDeclaredMethod("phasesDescMap") | ||
.invoke(global) | ||
.asInstanceOf[Map[SubComponent, String]] | ||
|
||
// Replace Global.analyzer to customize namer and typer (step 1 of 3) | ||
// | ||
// Unfortunately compiler plugins are instantiated too late. Therefore by now | ||
// analyzer has already been used to instantiate the "namer", "packageobjects" | ||
// and "typer" subcomponents. These are not phases yet -- they are just phase | ||
// factories -- so no disaster yet, but we have to be quick. This warrants the | ||
// second step in this customization - rewiring phase factories. | ||
val analyzer = new { val global = Plugin.this.global } with Analyzer | ||
val analyzerField = classOf[Global].getDeclaredField("analyzer") | ||
analyzerField.setAccessible(true) | ||
analyzerField.set(global, analyzer) | ||
|
||
// Replace Global.analyzer to customize namer and typer (step 2 of 3) | ||
// | ||
// Luckily for us compiler plugins are instantiated quite early. So by now, | ||
// internal phases have only left a trace in "phasesSet" and in | ||
// "phasesDescMap". Also, up until now no one has really used the standard | ||
// analyzer, so we're almost all set except for the standard | ||
// `object typer extends analyzer.Typer(<some default context>)` | ||
// that is a member of `Global` and hence has been pre-initialized now. | ||
// Good news is that it's only used in later phases or as a host for less | ||
// important activities (error reporting, printing, etc). | ||
val phasesSetMapGetter = classOf[Global].getDeclaredMethod("phasesSet") | ||
val phasesSet = phasesSetMapGetter.invoke(global).asInstanceOf[Set[SubComponent]] | ||
// `scalac -help` doesn't instantiate standard phases | ||
if (phasesSet exists { _.phaseName == "typer" }) { | ||
def subComponentByName(name: String) = | ||
(phasesSet find { _.phaseName == name }).head | ||
def hijackDescription(pt: SubComponent, sc: SubComponent) = | ||
phasesDescMap(sc) = phasesDescMap(pt) + " in dslparadise" | ||
|
||
val oldScs @ List(oldNamer, oldPackageobjects, oldTyper) = List( | ||
subComponentByName("namer"), subComponentByName("packageobjects"), subComponentByName("typer")) | ||
val newScs = List( | ||
analyzer.namerFactory, analyzer.packageObjects, analyzer.typerFactory) | ||
oldScs zip newScs foreach { case (pt, sc) => hijackDescription(pt, sc) } | ||
phasesSet --= oldScs | ||
phasesSet ++= newScs | ||
} | ||
|
||
// Replace Global.analyzer to customize namer and typer (step 3 of 3) | ||
// | ||
// Now let's take a look at what we couldn't replace during steps 1 and 2. | ||
// here's what gets printed if we add the following line | ||
// `if (!getClass.getName.startsWith("org.dslparadise")) println(getClass.getName)` | ||
// to the standard namer and typer classes | ||
// | ||
// scala.tools.nsc.Global$typer$ | ||
// scala.tools.nsc.typechecker.Implicits$ImplicitSearch | ||
// ... | ||
// scala.tools.nsc.transform.Erasure$Eraser | ||
// ... | ||
// scala.tools.nsc.typechecker.Namers$NormalNamer | ||
// scala.tools.nsc.transform.Erasure$Eraser | ||
// scala.tools.nsc.transform.Erasure$Eraser | ||
// scala.tools.nsc.typechecker.Namers$NormalNamer | ||
// ... | ||
// | ||
// Duh, we're still not done. But the good news is that it doesn't matter for | ||
// DSL paradise: | ||
// 1) ImplicitSearch is easily worked around by overriding `inferImplicit` and | ||
// `allViewsFrom` | ||
// 2) `scala.tools.nsc.Global$typer$` is only used in later phases or as a | ||
// host for less important activities (error reporting, printing, etc) | ||
// 3) Custom erasure typer and namers it spawns are also only used in later | ||
// phases | ||
// | ||
// TODO: Theoretically, points 2 and 3 can still be customizable. | ||
// `Global.typer` can have itself set as a specialized subclass of | ||
// `scala.tools.nsc.Global$typer$` (it's possible because by now it's still | ||
// null, as object initialization is lazy). Erasure can be hijacked in the | ||
// same way as we hijack "namer", "packageobjects" and "typer". However, | ||
// for now this is all not essential, so I'm moving on | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
<plugin> | ||
<name>dslparadise</name> | ||
<classname>dslparadise.Plugin</classname> | ||
</plugin> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package object dslparadise { | ||
import scala.language.implicitConversions | ||
|
||
trait ImplicitFunction1[-T, +R] extends Function1[T, R] | ||
|
||
object ImplicitFunction1 { | ||
implicit def apply[T, R](f: T => R): T `implicit =>` R = | ||
new ImplicitFunction1[T, R] { | ||
def apply(v: T): R = f(v) | ||
} | ||
} | ||
|
||
type `implicit =>`[-T, +R] = ImplicitFunction1[T, R] | ||
|
||
|
||
// trait ImportFunction1[-T, +R] extends Function1[T, R] | ||
// | ||
// object ImportFunction1 { | ||
// implicit def apply[T, R](f: T => R): T `import._ =>` R = | ||
// new ImportFunction1[T, R] { | ||
// def apply(v: T): R = f(v) | ||
// } | ||
// } | ||
// | ||
// type `import._ =>`[-T, +R] = ImportFunction1[T, R] | ||
// | ||
// | ||
// trait ImportValue[+T, +I] { | ||
// val value: T | ||
// } | ||
// | ||
// object ImportValue { | ||
// implicit def apply[T](v: T): T `import._` Nothing = | ||
// new ImportValue[T, Nothing] { | ||
// val value: T = v | ||
// } | ||
// } | ||
// | ||
// type `import._`[+T, +I] = ImportValue[T, I] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package dslparadise | ||
package typechecker | ||
|
||
import scala.tools.nsc.typechecker.{Analyzer => NscAnalyzer} | ||
|
||
trait Analyzer extends NscAnalyzer with Typers { | ||
override def newTyper(context: Context) = | ||
new Typer(context) with ParadiseTyper | ||
} |
42 changes: 42 additions & 0 deletions
42
plugin/src/main/scala/dslparadise/typechecker/Typers.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package dslparadise | ||
package typechecker | ||
|
||
import scala.reflect.internal.Mode | ||
import dslparadise._ | ||
|
||
trait Typers { | ||
self: Analyzer => | ||
|
||
import global._ | ||
|
||
trait ParadiseTyper extends Typer with TyperContextErrors { | ||
override def typedArg(arg: Tree, mode: Mode, newmode: Mode, pt: Type): Tree = { | ||
if (pt != WildcardType && pt <:< typeOf[ImplicitFunction1[_, _]]) { | ||
val convertArg = context inSilentMode { | ||
super.typedArg(arg.duplicate, mode, newmode, pt) | ||
context.reporter.hasErrors | ||
} | ||
|
||
val newarg = if (convertArg) { | ||
val newarg = | ||
q"""_root_.dslparadise.ImplicitFunction1 { | ||
implicit! => $arg | ||
}""" | ||
|
||
val keepArg = context inSilentMode { | ||
super.typedArg(newarg.duplicate, mode, newmode, pt) | ||
context.reporter.errors exists { _.errPos == NoPosition } | ||
} | ||
|
||
if (keepArg) arg else newarg | ||
} | ||
else | ||
arg | ||
|
||
super.typedArg(newarg, mode, newmode, pt) | ||
} | ||
else | ||
super.typedArg(arg, mode, newmode, pt) | ||
} | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
7 changes: 0 additions & 7 deletions
7
plugin/src/main/scala/org/dslparadise/annotations/Annotations.scala
This file was deleted.
Oops, something went wrong.
4 changes: 0 additions & 4 deletions
4
plugin/src/main/scala/org/dslparadise/embedded/scalac-plugin.xml
This file was deleted.
Oops, something went wrong.
13 changes: 0 additions & 13 deletions
13
plugin/src/main/scala/org/dslparadise/reflect/Enrichments.scala
This file was deleted.
Oops, something went wrong.
13 changes: 0 additions & 13 deletions
13
plugin/src/main/scala/org/dslparadise/typechecker/Analyzer.scala
This file was deleted.
Oops, something went wrong.
19 changes: 0 additions & 19 deletions
19
plugin/src/main/scala/org/dslparadise/typechecker/ContextErrors.scala
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.