Created
November 14, 2016 07:15
-
-
Save notxcain/d52d0e0a9692ab87aee3b41925b9466a to your computer and use it in GitHub Desktop.
Free Interpreter Gen
This file contains hidden or 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
| @free sealed trait Op[A] | |
| object Op { | |
| final case class StringOp(s: String) extends Op[String] | |
| final case class PolyOp[A](a: A) extends Op[A] | |
| /* Should be expanded to | |
| trait ForF[F[_]] { | |
| def stringOp(s: String): F[String] | |
| def polyOp[A](a: A): F[A] | |
| } | |
| */ | |
| } |
This file contains hidden or 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
| import scala.collection.immutable.Seq | |
| import scala.meta._ | |
| class free extends scala.annotation.StaticAnnotation { | |
| inline def apply(defn: Any): Any = meta { | |
| defn match { | |
| case Term.Block(Seq(t @ ClassOrTraitWithOneTypeParameter(mods, name), companion: Defn.Object)) | |
| if FreeMacro.isSealed(mods) => | |
| val oldTemplStats = companion.templ.stats.getOrElse(Nil) | |
| val subTypes = oldTemplStats.collect { | |
| case t: Defn.Class if FreeMacro.inherits(name)(t) => t | |
| } | |
| val newStats = | |
| FreeMacro.mkTraitF(name, subTypes) +: oldTemplStats | |
| val newCompanion = | |
| companion.copy(templ = companion.templ.copy(stats = Some(newStats))) | |
| println(newCompanion) | |
| Term.Block(Seq(t, newCompanion)) | |
| } | |
| } | |
| } | |
| object FreeMacro { | |
| def isSealed(mods: Seq[Mod]): Boolean = mods.exists(_.syntax == "sealed") | |
| def inherits(superType: Type.Name)(cls: Defn.Class): Boolean = | |
| cls.templ.parents.headOption.exists { | |
| case q"$parent[$out]()" => | |
| parent.syntax == superType.syntax | |
| case x => false | |
| } | |
| def mkTraitF(superName: Type.Name, subTypes: Seq[Defn.Class]): Stat = { | |
| def decapitalize(string: String): String = | |
| string.head.toLower + string.tail | |
| val stats = subTypes.map { | |
| case q"..$mods class $name[..$tparams](..$fields) extends $f[$tout]()" => | |
| q"def ${Term.Name(decapitalize(name.toString))}[..$tparams](..$fields): F[$tout]" | |
| } | |
| q""" | |
| trait ForF[F[_]] { | |
| ..$stats | |
| } | |
| """ | |
| } | |
| } | |
| object ClassOrTraitWithOneTypeParameter { | |
| def unapply(any: Defn): Option[(Seq[Mod], Type.Name)] = any match { | |
| case t: Defn.Class if t.tparams.length == 1 => Some((t.mods, t.name)) | |
| case t: Defn.Trait if t.tparams.length == 1 => Some((t.mods, t.name)) | |
| case x => | |
| println(x) | |
| None | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment