diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..7a73a41 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/project/build.properties b/project/build.properties index d1798b3..e98ac44 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version = 1.0.4 \ No newline at end of file +sbt.version = 1.1.0-RC4 diff --git a/scalagen/src/main/scala/org/scalameta/scalagen/GeneratorApplication.scala b/scalagen/src/main/scala/org/scalameta/scalagen/GeneratorApplication.scala new file mode 100644 index 0000000..05d01cc --- /dev/null +++ b/scalagen/src/main/scala/org/scalameta/scalagen/GeneratorApplication.scala @@ -0,0 +1,74 @@ +package org.scalameta.scalagen + +import scala.meta._ +import scala.meta.contrib._ +import scala.meta.gen._ + +/** + * All transformations, manipulations etc. should completely noop, by default. + * + * They should return the exact identical object thats inputed. + * + * Structural equality is slow, thus we want to remain as efficient as possible here. + */ +object GeneratorApplication { + + def apply(c: Defn.Class, g: Generator): Defn.Class = { + + generateCompanionExtensions(c, g) + + val extended: Defn.Class = c.prepend(g.extend(c)) + val transformed: Defn.Class = g.manipulate(c) + val transmuted: List[Stat] = g.transmute(c) + + val wasExtended = extended ne c + val wasTransformed = transformed ne c + val wasTransmuted = !(transmuted.lengthCompare(1) == 0 && (transmuted.head eq c)) + + if (wasTransformedMultipleTimes(wasExtended, wasTransformed, wasTransmuted)) { + abortDueToMultipleTransform(c, g) + } + + if (wasExtended) { + extended + } else if (wasTransformed) { + transformed + } else if (wasTransmuted) { + populateTransmutations(c, transmuted, g) + c + } else { + c + } + } + + private def abortDueToMultipleTransform(c: Defn.Class, g: Generator) = { + abort( + s"""Generator: ${g.name} tried to transform the tree ${c.name.syntax} multiple times + | + |This error can appear when a generator implements more then one of the following + | + | extend() + | transmute() + | manipulate() + | + """.stripMargin) + } + + private def wasTransformedMultipleTimes(booleans: Boolean*) = + booleans.count(_ == true) > 1 + + private def populateTransmutations(t: Tree, stats: List[Stat], g: Generator): Unit = { + t.owner match { + case Some(own) => ??? + case _ => + } + } + + private def generateCompanionExtensions(c: Defn.Class, g: Generator): Unit = { + g.extendCompanion(c) match { + case Nil => + case other => ??? + } + } + +} diff --git a/scalagen/src/main/scala/org/scalameta/scalagen/Runner.scala b/scalagen/src/main/scala/org/scalameta/scalagen/Runner.scala index 92fd8c3..adce020 100644 --- a/scalagen/src/main/scala/org/scalameta/scalagen/Runner.scala +++ b/scalagen/src/main/scala/org/scalameta/scalagen/Runner.scala @@ -383,16 +383,7 @@ case class Runner(generators: Set[Generator], recurse: Boolean = false) { private def findGenerators[B <: Tree: ModExtractor](a: B): List[Generator] = { a.extract[Mod].collect { case Mod.Annot(Init(Type.Name(n), _, _)) if generator_cache.contains(n) => - generator_cache(n) match { - case m: ManipulationGenerator => m - case e: ExtensionGenerator => e - case t: TransmutationGenerator => t - case c: CompanionGenerator => c - case p: ParameterGenerator => p - case g => - throw new IllegalStateException( - s"The runner cannot handle this type of generator: ${g.getClass.getSimpleName}") - } + generator_cache(n) } } } diff --git a/scalagen/src/main/scala/scala/meta/gen/Generators.scala b/scalagen/src/main/scala/scala/meta/gen/Generators.scala index cc49765..5b2c36e 100644 --- a/scalagen/src/main/scala/scala/meta/gen/Generators.scala +++ b/scalagen/src/main/scala/scala/meta/gen/Generators.scala @@ -3,9 +3,18 @@ package scala.meta.gen import scala.annotation.StaticAnnotation import scala.meta._ -trait Generator extends StaticAnnotation { - def name: String -} +/** + * All Generators should extend this class. + * + * The traits are just different types of expansion + */ +abstract class Generator(val name: String) + extends ExtensionGeneratorApi + with CompanionGeneratorApi + with ManipulationGeneratorApi + with TransmutationGeneratorApi + with ParameterGeneratorApi + with StaticAnnotation /** * Use this trait for extending existing definitions. @@ -14,13 +23,13 @@ trait Generator extends StaticAnnotation { * * Default: Add no new stats */ -abstract class ExtensionGenerator(val name: String) extends Generator { +trait ExtensionGeneratorApi { def extend(c: Defn.Class): List[Stat] = Nil def extend(t: Defn.Trait): List[Stat] = Nil def extend(o: Defn.Object): List[Stat] = Nil } -object IdentityGenerator extends ExtensionGenerator("Identity") +object IdentityGenerator extends Generator("Identity") /** * Use this trait for extending the case class of a Defn. @@ -31,7 +40,7 @@ object IdentityGenerator extends ExtensionGenerator("Identity") * * Note: These *will* generate a companion if one does not exist. */ -abstract class CompanionGenerator(val name: String) extends Generator { +trait CompanionGeneratorApi { def extendCompanion(c: Defn.Class): List[Stat] = Nil def extendCompanion(c: Defn.Type): List[Stat] = Nil def extendCompanion(c: Defn.Trait): List[Stat] = Nil @@ -48,7 +57,7 @@ abstract class CompanionGenerator(val name: String) extends Generator { * * Default: return the input */ -abstract class ManipulationGenerator(val name: String) extends Generator { +trait ManipulationGeneratorApi { def manipulate(c: Defn.Class): Defn.Class = c def manipulate(t: Defn.Trait): Defn.Trait = t def manipulate(t: Defn.Type): Defn.Type = t @@ -75,7 +84,7 @@ abstract class ManipulationGenerator(val name: String) extends Generator { * * Default: return the input */ -abstract class TransmutationGenerator(val name: String) extends Generator { +trait TransmutationGeneratorApi{ def transmute(c: Defn.Class): List[Stat] = c :: Nil def transmute(t: Defn.Trait): List[Stat] = t :: Nil def transmute(t: Defn.Type): List[Stat] = t :: Nil @@ -97,7 +106,7 @@ abstract class TransmutationGenerator(val name: String) extends Generator { * * Default: No stats added */ -abstract class ParameterGenerator(val name: String) extends Generator { +trait ParameterGeneratorApi { def extend(p: Type.Param): List[Stat] = Nil def extend(p: Term.Param): List[Stat] = Nil } diff --git a/scalagen/src/main/scala/scala/meta/gen/implicits/ExtractAndReplaceExtras.scala b/scalagen/src/main/scala/scala/meta/gen/implicits/ExtractAndReplaceExtras.scala new file mode 100644 index 0000000..98850ee --- /dev/null +++ b/scalagen/src/main/scala/scala/meta/gen/implicits/ExtractAndReplaceExtras.scala @@ -0,0 +1,18 @@ +package scala.meta.gen.implicits + +import scala.meta.contrib._ + +trait ExtractAndReplaceExtras { + + implicit class XtensionExtractAndReplace[A](a: A) { + def prepend[B](bs: List[B])(implicit rev: Replace[A, B], eev: Extract[A, B]): A = + replaceShortCircuited(bs ::: eev.extract(a)) + + def replaceShortCircuited[B](bs: List[B])(implicit rev: Replace[A, B], eev: Extract[A, B]): A = + if (eev.extract(a) eq bs) { + a + } else { + rev.replace(a, bs) + } + } +} \ No newline at end of file diff --git a/scalagen/src/main/scala/scala/meta/gen/implicits/implicits.scala b/scalagen/src/main/scala/scala/meta/gen/implicits/implicits.scala index 02cbd06..420280d 100644 --- a/scalagen/src/main/scala/scala/meta/gen/implicits/implicits.scala +++ b/scalagen/src/main/scala/scala/meta/gen/implicits/implicits.scala @@ -1,3 +1,3 @@ package scala.meta.gen.implicits -trait implicits extends Traversal with Owner with FindCompanion +trait implicits extends Traversal with Owner with FindCompanion with ExtractAndReplaceExtras