Skip to content
This repository has been archived by the owner on Apr 18, 2020. It is now read-only.

WIP: Very early preview of generator merging #45

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{
}
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version = 1.0.4
sbt.version = 1.1.0-RC4
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1.1.0 is out now! 🎉

Original file line number Diff line number Diff line change
@@ -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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's possible to implement much faster structural equality than what we currently have, it will still be nut slow compared to referential equality. productPrefix and productIterator and scala.runtime.Statics should do the job.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

another undocumented point is that Tree.origin (private[meta]) contains the position and is preserved during tree transforms, this can also be used to test equality.

*/
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 => ???
}
}

}
11 changes: 1 addition & 10 deletions scalagen/src/main/scala/org/scalameta/scalagen/Runner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}
27 changes: 18 additions & 9 deletions scalagen/src/main/scala/scala/meta/gen/Generators.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
}
Original file line number Diff line number Diff line change
@@ -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)
}
}
}
Original file line number Diff line number Diff line change
@@ -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