Skip to content

Instantly share code, notes, and snippets.

@notxcain
Created November 14, 2016 07:15
Show Gist options
  • Select an option

  • Save notxcain/d52d0e0a9692ab87aee3b41925b9466a to your computer and use it in GitHub Desktop.

Select an option

Save notxcain/d52d0e0a9692ab87aee3b41925b9466a to your computer and use it in GitHub Desktop.
Free Interpreter Gen
@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]
}
*/
}
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